diff options
Diffstat (limited to 'core')
202 files changed, 8138 insertions, 5086 deletions
diff --git a/core/SCsub b/core/SCsub index d9167b8f83..c12dd4e60e 100644 --- a/core/SCsub +++ b/core/SCsub @@ -146,6 +146,7 @@ env.core_sources += thirdparty_obj  # Godot source files  env.add_source_files(env.core_sources, "*.cpp") +env.add_source_files(env.core_sources, "script_encryption_key.gen.cpp")  # Certificates  env.Depends( @@ -183,6 +184,7 @@ SConscript("os/SCsub")  SConscript("math/SCsub")  SConscript("crypto/SCsub")  SConscript("io/SCsub") +SConscript("multiplayer/SCsub")  SConscript("debugger/SCsub")  SConscript("input/SCsub")  SConscript("variant/SCsub") diff --git a/core/config/engine.cpp b/core/config/engine.cpp index d8fbb50a75..dc5b3e25c6 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -111,7 +111,7 @@ Dictionary Engine::get_version_info() const {  static Array array_from_info(const char *const *info_list) {  	Array arr;  	for (int i = 0; info_list[i] != nullptr; i++) { -		arr.push_back(info_list[i]); +		arr.push_back(String::utf8(info_list[i]));  	}  	return arr;  } @@ -119,7 +119,7 @@ static Array array_from_info(const char *const *info_list) {  static Array array_from_info_count(const char *const *info_list, int info_count) {  	Array arr;  	for (int i = 0; i < info_count; i++) { -		arr.push_back(info_list[i]); +		arr.push_back(String::utf8(info_list[i]));  	}  	return arr;  } @@ -140,14 +140,14 @@ Array Engine::get_copyright_info() const {  	for (int component_index = 0; component_index < COPYRIGHT_INFO_COUNT; component_index++) {  		const ComponentCopyright &cp_info = COPYRIGHT_INFO[component_index];  		Dictionary component_dict; -		component_dict["name"] = cp_info.name; +		component_dict["name"] = String::utf8(cp_info.name);  		Array parts;  		for (int i = 0; i < cp_info.part_count; i++) {  			const ComponentCopyrightPart &cp_part = cp_info.parts[i];  			Dictionary part_dict;  			part_dict["files"] = array_from_info_count(cp_part.files, cp_part.file_count);  			part_dict["copyright"] = array_from_info_count(cp_part.copyright_statements, cp_part.copyright_count); -			part_dict["license"] = cp_part.license; +			part_dict["license"] = String::utf8(cp_part.license);  			parts.push_back(part_dict);  		}  		component_dict["parts"] = parts; diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 03892d1d4f..c85b0866cb 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -41,6 +41,11 @@  #include "core/os/keyboard.h"  #include "core/os/os.h"  #include "core/variant/variant_parser.h" +#include "core/version.h" + +#include "modules/modules_enabled.gen.h" // For mono. + +const String ProjectSettings::PROJECT_DATA_DIR_NAME_SUFFIX = "godot";  ProjectSettings *ProjectSettings::singleton = nullptr; @@ -48,18 +53,95 @@ ProjectSettings *ProjectSettings::get_singleton() {  	return singleton;  } +String ProjectSettings::get_project_data_dir_name() const { +	return project_data_dir_name; +} + +String ProjectSettings::get_project_data_path() const { +	return "res://" + get_project_data_dir_name(); +} +  String ProjectSettings::get_resource_path() const {  	return resource_path;  } -const String ProjectSettings::IMPORTED_FILES_PATH("res://.godot/imported"); +String ProjectSettings::get_safe_project_name() const { +	String safe_name = OS::get_singleton()->get_safe_dir_name(get("application/config/name")); +	if (safe_name.is_empty()) { +		safe_name = "UnnamedProject"; +	} +	return safe_name; +} -String ProjectSettings::localize_path(const String &p_path) const { -	if (resource_path == "") { -		return p_path; //not initialized yet +String ProjectSettings::get_imported_files_path() const { +	return get_project_data_path().plus_file("imported"); +} + +// Returns the features that a project must have when opened with this build of Godot. +// This is used by the project manager to provide the initial_settings for config/features. +const PackedStringArray ProjectSettings::get_required_features() { +	PackedStringArray features = PackedStringArray(); +	features.append(VERSION_BRANCH); +#ifdef REAL_T_IS_DOUBLE +	features.append("Double Precision"); +#endif +	return features; +} + +// Returns the features supported by this build of Godot. Includes all required features. +const PackedStringArray ProjectSettings::_get_supported_features() { +	PackedStringArray features = get_required_features(); +#ifdef MODULE_MONO_ENABLED +	features.append("C#"); +#endif +	// Allow pinning to a specific patch number or build type by marking +	// them as supported. They're only used if the user adds them manually. +	features.append(VERSION_BRANCH "." _MKSTR(VERSION_PATCH)); +	features.append(VERSION_FULL_CONFIG); +	features.append(VERSION_FULL_BUILD); +	// For now, assume Vulkan is always supported. +	// This should be removed if it's possible to build the editor without Vulkan. +	features.append("Vulkan Clustered"); +	features.append("Vulkan Mobile"); +	return features; +} + +// Returns the features that this project needs but this build of Godot lacks. +const PackedStringArray ProjectSettings::get_unsupported_features(const PackedStringArray &p_project_features) { +	PackedStringArray unsupported_features = PackedStringArray(); +	PackedStringArray supported_features = singleton->_get_supported_features(); +	for (int i = 0; i < p_project_features.size(); i++) { +		if (!supported_features.has(p_project_features[i])) { +			unsupported_features.append(p_project_features[i]); +		}  	} +	unsupported_features.sort(); +	return unsupported_features; +} -	if (p_path.begins_with("res://") || p_path.begins_with("user://") || +// Returns the features that both this project has and this build of Godot has, ensuring required features exist. +const PackedStringArray ProjectSettings::_trim_to_supported_features(const PackedStringArray &p_project_features) { +	// Remove unsupported features if present. +	PackedStringArray features = PackedStringArray(p_project_features); +	PackedStringArray supported_features = _get_supported_features(); +	for (int i = p_project_features.size() - 1; i > -1; i--) { +		if (!supported_features.has(p_project_features[i])) { +			features.remove_at(i); +		} +	} +	// Add required features if not present. +	PackedStringArray required_features = get_required_features(); +	for (int i = 0; i < required_features.size(); i++) { +		if (!features.has(required_features[i])) { +			features.append(required_features[i]); +		} +	} +	features.sort(); +	return features; +} + +String ProjectSettings::localize_path(const String &p_path) const { +	if (resource_path.is_empty() || p_path.begins_with("res://") || p_path.begins_with("user://") ||  			(p_path.is_absolute_path() && !p_path.begins_with(resource_path))) {  		return p_path.simplify_path();  	} @@ -102,7 +184,7 @@ String ProjectSettings::localize_path(const String &p_path) const {  		String parent = path.substr(0, sep);  		String plocal = localize_path(parent); -		if (plocal == "") { +		if (plocal.is_empty()) {  			return "";  		}  		// Only strip the starting '/' from 'path' if its parent ('plocal') ends with '/' @@ -146,13 +228,13 @@ bool ProjectSettings::get_ignore_value_in_docs(const String &p_name) const {  String ProjectSettings::globalize_path(const String &p_path) const {  	if (p_path.begins_with("res://")) { -		if (resource_path != "") { +		if (!resource_path.is_empty()) {  			return p_path.replace("res:/", resource_path);  		}  		return p_path.replace("res://", "");  	} else if (p_path.begins_with("user://")) {  		String data_dir = OS::get_singleton()->get_user_data_dir(); -		if (data_dir != "") { +		if (!data_dir.is_empty()) {  			return p_path.replace("user:/", data_dir);  		}  		return p_path.replace("user://", ""); @@ -256,15 +338,15 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const {  	Set<_VCSort> vclist; -	for (Map<StringName, VariantContainer>::Element *E = props.front(); E; E = E->next()) { -		const VariantContainer *v = &E->get(); +	for (const KeyValue<StringName, VariantContainer> &E : props) { +		const VariantContainer *v = &E.value;  		if (v->hide_from_editor) {  			continue;  		}  		_VCSort vc; -		vc.name = E->key(); +		vc.name = E.key;  		vc.order = v->order;  		vc.type = v->variant.get_type();  		if (vc.name.begins_with("input/") || vc.name.begins_with("import/") || vc.name.begins_with("export/") || vc.name.begins_with("/remap") || vc.name.begins_with("/locale") || vc.name.begins_with("/autoload")) { @@ -322,14 +404,14 @@ bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_f  void ProjectSettings::_convert_to_last_version(int p_from_version) {  	if (p_from_version <= 3) {  		// Converts the actions from array to dictionary (array of events to dictionary with deadzone + events) -		for (Map<StringName, ProjectSettings::VariantContainer>::Element *E = props.front(); E; E = E->next()) { -			Variant value = E->get().variant; -			if (String(E->key()).begins_with("input/") && value.get_type() == Variant::ARRAY) { +		for (KeyValue<StringName, ProjectSettings::VariantContainer> &E : props) { +			Variant value = E.value.variant; +			if (String(E.key).begins_with("input/") && value.get_type() == Variant::ARRAY) {  				Array array = value;  				Dictionary action;  				action["deadzone"] = Variant(0.5f);  				action["events"] = array; -				E->get().variant = action; +				E.value.variant = action;  			}  		}  	} @@ -360,12 +442,12 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) {   *    If a project file is found, load it or fail.   *    If nothing was found, error out.   */ -Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, bool p_upwards) { +Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, bool p_upwards, bool p_ignore_override) {  	// If looking for files in a network client, use it directly  	if (FileAccessNetworkClient::get_singleton()) {  		Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); -		if (err == OK) { +		if (err == OK && !p_ignore_override) {  			// Optional, we don't mind if it fails  			_load_settings_text("res://override.cfg");  		} @@ -374,12 +456,12 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b  	// Attempt with a user-defined main pack first -	if (p_main_pack != "") { +	if (!p_main_pack.is_empty()) {  		bool ok = _load_resource_pack(p_main_pack);  		ERR_FAIL_COND_V_MSG(!ok, ERR_CANT_OPEN, "Cannot open resource pack '" + p_main_pack + "'.");  		Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); -		if (err == OK) { +		if (err == OK && !p_ignore_override) {  			// Load override from location of the main pack  			// Optional, we don't mind if it fails  			_load_settings_text(p_main_pack.get_base_dir().plus_file("override.cfg")); @@ -389,7 +471,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b  	String exec_path = OS::get_singleton()->get_executable_path(); -	if (exec_path != "") { +	if (!exec_path.is_empty()) {  		// We do several tests sequentially until one succeeds to find a PCK,  		// and if so, we attempt loading it at the end. @@ -429,7 +511,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b  		// If we opened our package, try and load our project.  		if (found) {  			Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); -			if (err == OK) { +			if (err == OK && !p_ignore_override) {  				// Load override from location of the executable.  				// Optional, we don't mind if it fails.  				_load_settings_text(exec_path.get_base_dir().plus_file("override.cfg")); @@ -441,16 +523,16 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b  	// Try to use the filesystem for files, according to OS.  	// (Only Android -when reading from pck- and iOS use this.) -	if (OS::get_singleton()->get_resource_dir() != "") { +	if (!OS::get_singleton()->get_resource_dir().is_empty()) {  		// OS will call ProjectSettings->get_resource_path which will be empty if not overridden!  		// If the OS would rather use a specific location, then it will not be empty.  		resource_path = OS::get_singleton()->get_resource_dir().replace("\\", "/"); -		if (resource_path != "" && resource_path[resource_path.length() - 1] == '/') { +		if (!resource_path.is_empty() && resource_path[resource_path.length() - 1] == '/') {  			resource_path = resource_path.substr(0, resource_path.length() - 1); // Chop end.  		}  		Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); -		if (err == OK) { +		if (err == OK && !p_ignore_override) {  			// Optional, we don't mind if it fails.  			_load_settings_text("res://override.cfg");  		} @@ -473,7 +555,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b  		resource_path = current_dir;  		resource_path = resource_path.replace("\\", "/"); // Windows path to Unix path just in case.  		err = _load_settings_text_or_binary(current_dir.plus_file("project.godot"), current_dir.plus_file("project.binary")); -		if (err == OK) { +		if (err == OK && !p_ignore_override) {  			// Optional, we don't mind if it fails.  			_load_settings_text(current_dir.plus_file("override.cfg"));  			found = true; @@ -505,14 +587,19 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b  	return OK;  } -Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bool p_upwards) { -	Error err = _setup(p_path, p_main_pack, p_upwards); +Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bool p_upwards, bool p_ignore_override) { +	Error err = _setup(p_path, p_main_pack, p_upwards, p_ignore_override);  	if (err == OK) {  		String custom_settings = GLOBAL_DEF("application/config/project_settings_override", ""); -		if (custom_settings != "") { +		if (!custom_settings.is_empty()) {  			_load_settings_text(custom_settings);  		}  	} + +	// Updating the default value after the project settings have loaded. +	bool use_hidden_directory = GLOBAL_GET("application/config/use_hidden_project_data_directory"); +	project_data_dir_name = (use_hidden_directory ? "." : "") + PROJECT_DATA_DIR_NAME_SUFFIX; +  	// 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"); @@ -612,21 +699,21 @@ Error ProjectSettings::_load_settings_text(const String &p_path) {  			return err;  		} -		if (assign != String()) { -			if (section == String() && assign == "config_version") { +		if (!assign.is_empty()) { +			if (section.is_empty() && assign == "config_version") {  				config_version = value;  				if (config_version > CONFIG_VERSION) {  					memdelete(f);  					ERR_FAIL_V_MSG(ERR_FILE_CANT_OPEN, vformat("Can't open project at '%s', its `config_version` (%d) is from a more recent and incompatible version of the engine. Expected config version: %d.", p_path, config_version, CONFIG_VERSION));  				}  			} else { -				if (section == String()) { +				if (section.is_empty()) {  					set(assign, value);  				} else {  					set(section + "/" + assign, value);  				}  			} -		} else if (next_tag.name != String()) { +		} else if (!next_tag.name.is_empty()) {  			section = next_tag.name;  		}  	} @@ -653,6 +740,13 @@ Error ProjectSettings::_load_settings_text_or_binary(const String &p_text_path,  	return err;  } +Error ProjectSettings::load_custom(const String &p_path) { +	if (p_path.ends_with(".binary")) { +		return _load_settings_binary(p_path); +	} +	return _load_settings_text(p_path); +} +  int ProjectSettings::get_order(const String &p_name) const {  	ERR_FAIL_COND_V_MSG(!props.has(p_name), -1, "Request for nonexistent project setting: " + p_name + ".");  	return props[p_name].order; @@ -699,11 +793,11 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str  	int count = 0; -	for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) { -		count += E->get().size(); +	for (const KeyValue<String, List<String>> &E : props) { +		count += E.value.size();  	} -	if (p_custom_features != String()) { +	if (!p_custom_features.is_empty()) {  		file->store_32(count + 1);  		//store how many properties are saved, add one for custom featuers, which must always go first  		String key = CoreStringNames::get_singleton()->_custom_features; @@ -733,7 +827,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str  	for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) {  		for (String &key : E->get()) { -			if (E->key() != "") { +			if (!E->key().is_empty()) {  				key = E->key() + "/" + key;  			}  			Variant value; @@ -787,22 +881,22 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin  	file->store_line("");  	file->store_string("config_version=" + itos(CONFIG_VERSION) + "\n"); -	if (p_custom_features != String()) { +	if (!p_custom_features.is_empty()) {  		file->store_string("custom_features=\"" + p_custom_features + "\"\n");  	}  	file->store_string("\n"); -	for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) { +	for (const Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) {  		if (E != props.front()) {  			file->store_string("\n");  		} -		if (E->key() != "") { +		if (!E->key().is_empty()) {  			file->store_string("[" + E->key() + "]\n\n");  		}  		for (const String &F : E->get()) {  			String key = F; -			if (E->key() != "") { +			if (!E->key().is_empty()) {  				key = E->key() + "/" + key;  			}  			Variant value; @@ -830,24 +924,52 @@ Error ProjectSettings::_save_custom_bnd(const String &p_file) { // add other par  }  Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_custom, const Vector<String> &p_custom_features, bool p_merge_with_current) { -	ERR_FAIL_COND_V_MSG(p_path == "", ERR_INVALID_PARAMETER, "Project settings save path cannot be empty."); +	ERR_FAIL_COND_V_MSG(p_path.is_empty(), ERR_INVALID_PARAMETER, "Project settings save path cannot be empty."); + +	PackedStringArray project_features = has_setting("application/config/features") ? (PackedStringArray)get_setting("application/config/features") : PackedStringArray(); +	// If there is no feature list currently present, force one to generate. +	if (project_features.is_empty()) { +		project_features = ProjectSettings::get_required_features(); +	} +	// Check the rendering API. +	const String rendering_api = has_setting("rendering/quality/driver/driver_name") ? (String)get_setting("rendering/quality/driver/driver_name") : String(); +	if (!rendering_api.is_empty()) { +		// Add the rendering API as a project feature if it doesn't already exist. +		if (!project_features.has(rendering_api)) { +			project_features.append(rendering_api); +		} +	} +	// Check for the existence of a csproj file. +	if (FileAccess::exists(get_resource_path().plus_file(get_safe_project_name() + ".csproj"))) { +		// If there is a csproj file, add the C# feature if it doesn't already exist. +		if (!project_features.has("C#")) { +			project_features.append("C#"); +		} +	} else { +		// If there isn't a csproj file, remove the C# feature if it exists. +		if (project_features.has("C#")) { +			project_features.remove_at(project_features.find("C#")); +		} +	} +	project_features = _trim_to_supported_features(project_features); +	set_setting("application/config/features", project_features);  	Set<_VCSort> vclist;  	if (p_merge_with_current) { -		for (Map<StringName, VariantContainer>::Element *G = props.front(); G; G = G->next()) { -			const VariantContainer *v = &G->get(); +		for (const KeyValue<StringName, VariantContainer> &G : props) { +			const VariantContainer *v = &G.value;  			if (v->hide_from_editor) {  				continue;  			} -			if (p_custom.has(G->key())) { +			if (p_custom.has(G.key)) {  				continue;  			}  			_VCSort vc; -			vc.name = G->key(); //*k; +			vc.name = G.key; //*k;  			vc.order = v->order;  			vc.type = v->variant.get_type();  			vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE; @@ -859,14 +981,14 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust  		}  	} -	for (const Map<String, Variant>::Element *E = p_custom.front(); E; E = E->next()) { +	for (const KeyValue<String, Variant> &E : p_custom) {  		// Lookup global prop to store in the same order -		Map<StringName, VariantContainer>::Element *global_prop = props.find(E->key()); +		Map<StringName, VariantContainer>::Element *global_prop = props.find(E.key);  		_VCSort vc; -		vc.name = E->key(); +		vc.name = E.key;  		vc.order = global_prop ? global_prop->get().order : 0xFFFFFFF; -		vc.type = E->get().get_type(); +		vc.type = E.value.get_type();  		vc.flags = PROPERTY_USAGE_STORAGE;  		vclist.insert(vc);  	} @@ -1084,6 +1206,7 @@ ProjectSettings::ProjectSettings() {  	custom_prop_info["application/run/main_scene"] = PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res");  	GLOBAL_DEF("application/run/disable_stdout", false);  	GLOBAL_DEF("application/run/disable_stderr", false); +	GLOBAL_DEF_RST("application/config/use_hidden_project_data_directory", true);  	GLOBAL_DEF("application/config/use_custom_user_dir", false);  	GLOBAL_DEF("application/config/custom_user_dir_name", "");  	GLOBAL_DEF("application/config/project_settings_override", ""); diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 7e93f26f0d..5b74356337 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -42,12 +42,14 @@ class ProjectSettings : public Object {  public:  	typedef Map<String, Variant> CustomMap; -	static const String IMPORTED_FILES_PATH; +	static const String PROJECT_DATA_DIR_NAME_SUFFIX;  	enum {  		//properties that are not for built in values begin from this value, so builtin ones are displayed first  		NO_BUILTIN_ORDER_BASE = 1 << 16  	}; +	const static PackedStringArray get_required_features(); +	const static PackedStringArray get_unsupported_features(const PackedStringArray &p_project_features);  	struct AutoloadInfo {  		StringName name; @@ -94,6 +96,8 @@ protected:  	OrderedHashMap<StringName, AutoloadInfo> autoloads; +	String project_data_dir_name; +  	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; @@ -109,13 +113,16 @@ protected:  	Error _save_custom_bnd(const String &p_file); +	const static PackedStringArray _get_supported_features(); +	const static PackedStringArray _trim_to_supported_features(const PackedStringArray &p_project_features); +  	void _convert_to_last_version(int p_from_version);  	bool _load_resource_pack(const String &p_pack, bool p_replace_files = true, int p_offset = 0);  	void _add_property_info_bind(const Dictionary &p_info); -	Error _setup(const String &p_path, const String &p_main_pack, bool p_upwards = false); +	Error _setup(const String &p_path, const String &p_main_pack, bool p_upwards = false, bool p_ignore_override = false);  	void _add_builtin_input_map(); @@ -123,7 +130,7 @@ protected:  	static void _bind_methods();  public: -	static const int CONFIG_VERSION = 4; +	static const int CONFIG_VERSION = 5;  	void set_setting(const String &p_setting, const Variant &p_value);  	Variant get_setting(const String &p_setting) const; @@ -141,7 +148,11 @@ public:  	bool property_can_revert(const String &p_name);  	Variant property_get_revert(const String &p_name); +	String get_project_data_dir_name() const; +	String get_project_data_path() const;  	String get_resource_path() const; +	String get_safe_project_name() const; +	String get_imported_files_path() const;  	static ProjectSettings *get_singleton(); @@ -151,8 +162,9 @@ public:  	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); +	Error setup(const String &p_path, const String &p_main_pack, bool p_upwards = false, bool p_ignore_override = false); +	Error load_custom(const String &p_path);  	Error save_custom(const String &p_path = "", const CustomMap &p_custom = CustomMap(), const Vector<String> &p_custom_features = Vector<String>(), bool p_merge_with_current = true);  	Error save();  	void set_custom_property_info(const String &p_prop, const PropertyInfo &p_info); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 83a3b80803..fe026ed38f 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -207,6 +207,10 @@ void OS::alert(const String &p_alert, const String &p_title) {  	::OS::get_singleton()->alert(p_alert, p_title);  } +void OS::crash(const String &p_message) { +	CRASH_NOW_MSG(p_message); +} +  String OS::get_executable_path() const {  	return ::OS::get_singleton()->get_executable_path();  } @@ -235,6 +239,19 @@ int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r  	return exitcode;  } +int OS::create_instance(const Vector<String> &p_arguments) { +	List<String> args; +	for (int i = 0; i < p_arguments.size(); i++) { +		args.push_back(p_arguments[i]); +	} +	::OS::ProcessID pid = 0; +	Error err = ::OS::get_singleton()->create_instance(args, &pid); +	if (err != OK) { +		return -1; +	} +	return pid; +} +  int OS::create_process(const String &p_path, const Vector<String> &p_arguments) {  	List<String> args;  	for (int i = 0; i < p_arguments.size(); i++) { @@ -286,6 +303,10 @@ String OS::get_locale() const {  	return ::OS::get_singleton()->get_locale();  } +String OS::get_locale_language() const { +	return ::OS::get_singleton()->get_locale_language(); +} +  String OS::get_model_name() const {  	return ::OS::get_singleton()->get_model_name();  } @@ -298,6 +319,10 @@ Error OS::set_thread_name(const String &p_name) {  	return ::Thread::get_caller_id();  }; +::Thread::ID OS::get_main_thread_id() const { +	return ::Thread::get_main_id(); +}; +  bool OS::has_feature(const String &p_feature) const {  	return ::OS::get_singleton()->has_feature(p_feature);  } @@ -485,15 +510,15 @@ String OS::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {  	return ::OS::get_singleton()->get_system_dir(::OS::SystemDir(p_dir), p_shared_storage);  } -String OS::get_keycode_string(uint32_t p_code) const { +String OS::get_keycode_string(Key p_code) const {  	return ::keycode_get_string(p_code);  } -bool OS::is_keycode_unicode(uint32_t p_unicode) const { -	return ::keycode_has_unicode(p_unicode); +bool OS::is_keycode_unicode(char32_t p_unicode) const { +	return ::keycode_has_unicode((Key)p_unicode);  } -int OS::find_keycode_from_string(const String &p_code) const { +Key OS::find_keycode_from_string(const String &p_code) const {  	return find_keycode(p_code);  } @@ -521,6 +546,7 @@ void OS::_bind_methods() {  	ClassDB::bind_method(D_METHOD("close_midi_inputs"), &OS::close_midi_inputs);  	ClassDB::bind_method(D_METHOD("alert", "text", "title"), &OS::alert, DEFVAL("Alert!")); +	ClassDB::bind_method(D_METHOD("crash", "message"), &OS::crash);  	ClassDB::bind_method(D_METHOD("set_low_processor_usage_mode", "enable"), &OS::set_low_processor_usage_mode);  	ClassDB::bind_method(D_METHOD("is_in_low_processor_usage_mode"), &OS::is_in_low_processor_usage_mode); @@ -533,6 +559,7 @@ void OS::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);  	ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr"), &OS::execute, DEFVAL(Array()), DEFVAL(false));  	ClassDB::bind_method(D_METHOD("create_process", "path", "arguments"), &OS::create_process); +	ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance);  	ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill);  	ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open);  	ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id); @@ -547,6 +574,7 @@ void OS::_bind_methods() {  	ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &OS::delay_usec);  	ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &OS::delay_msec);  	ClassDB::bind_method(D_METHOD("get_locale"), &OS::get_locale); +	ClassDB::bind_method(D_METHOD("get_locale_language"), &OS::get_locale_language);  	ClassDB::bind_method(D_METHOD("get_model_name"), &OS::get_model_name);  	ClassDB::bind_method(D_METHOD("is_userfs_persistent"), &OS::is_userfs_persistent); @@ -582,6 +610,7 @@ void OS::_bind_methods() {  	ClassDB::bind_method(D_METHOD("set_thread_name", "name"), &OS::set_thread_name);  	ClassDB::bind_method(D_METHOD("get_thread_caller_id"), &OS::get_thread_caller_id); +	ClassDB::bind_method(D_METHOD("get_main_thread_id"), &OS::get_main_thread_id);  	ClassDB::bind_method(D_METHOD("has_feature", "tag_name"), &OS::has_feature); @@ -598,8 +627,8 @@ void OS::_bind_methods() {  	ADD_PROPERTY_DEFAULT("low_processor_usage_mode", false);  	ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900); -	BIND_ENUM_CONSTANT(VIDEO_DRIVER_GLES2);  	BIND_ENUM_CONSTANT(VIDEO_DRIVER_VULKAN); +	BIND_ENUM_CONSTANT(VIDEO_DRIVER_OPENGL_3);  	BIND_ENUM_CONSTANT(DAY_SUNDAY);  	BIND_ENUM_CONSTANT(DAY_MONDAY); @@ -1072,79 +1101,79 @@ bool File::is_open() const {  }  String File::get_path() const { -	ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use, or is lacking read-write permission.");  	return f->get_path();  }  String File::get_path_absolute() const { -	ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use, or is lacking read-write permission.");  	return f->get_path_absolute();  }  void File::seek(int64_t p_position) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	ERR_FAIL_COND_MSG(p_position < 0, "Seek position must be a positive integer.");  	f->seek(p_position);  }  void File::seek_end(int64_t p_position) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	f->seek_end(p_position);  }  uint64_t File::get_position() const { -	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission.");  	return f->get_position();  }  uint64_t File::get_length() const { -	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission.");  	return f->get_length();  }  bool File::eof_reached() const { -	ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use, or is lacking read-write permission.");  	return f->eof_reached();  }  uint8_t File::get_8() const { -	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission.");  	return f->get_8();  }  uint16_t File::get_16() const { -	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission.");  	return f->get_16();  }  uint32_t File::get_32() const { -	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission.");  	return f->get_32();  }  uint64_t File::get_64() const { -	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission.");  	return f->get_64();  }  float File::get_float() const { -	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission.");  	return f->get_float();  }  double File::get_double() const { -	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission.");  	return f->get_double();  }  real_t File::get_real() const { -	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use, or is lacking read-write permission.");  	return f->get_real();  }  Vector<uint8_t> File::get_buffer(int64_t p_length) const {  	Vector<uint8_t> data; -	ERR_FAIL_COND_V_MSG(!f, data, "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, data, "File must be opened before use, or is lacking read-write permission.");  	ERR_FAIL_COND_V_MSG(p_length < 0, data, "Length of buffer cannot be smaller than 0.");  	if (p_length == 0) { @@ -1165,7 +1194,7 @@ Vector<uint8_t> File::get_buffer(int64_t p_length) const {  }  String File::get_as_text() const { -	ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use, or is lacking read-write permission.");  	String text;  	uint64_t original_pos = f->get_position(); @@ -1192,12 +1221,12 @@ String File::get_sha256(const String &p_path) const {  }  String File::get_line() const { -	ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, String(), "File must be opened before use, or is lacking read-write permission.");  	return f->get_line();  }  Vector<String> File::get_csv_line(const String &p_delim) const { -	ERR_FAIL_COND_V_MSG(!f, Vector<String>(), "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, Vector<String>(), "File must be opened before use, or is lacking read-write permission.");  	return f->get_csv_line(p_delim);  } @@ -1225,77 +1254,77 @@ Error File::get_error() const {  }  void File::store_8(uint8_t p_dest) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	f->store_8(p_dest);  }  void File::store_16(uint16_t p_dest) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	f->store_16(p_dest);  }  void File::store_32(uint32_t p_dest) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	f->store_32(p_dest);  }  void File::store_64(uint64_t p_dest) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	f->store_64(p_dest);  }  void File::store_float(float p_dest) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	f->store_float(p_dest);  }  void File::store_double(double p_dest) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	f->store_double(p_dest);  }  void File::store_real(real_t p_real) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	f->store_real(p_real);  }  void File::store_string(const String &p_string) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	f->store_string(p_string);  }  void File::store_pascal_string(const String &p_string) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	f->store_pascal_string(p_string);  }  String File::get_pascal_string() { -	ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, "", "File must be opened before use, or is lacking read-write permission.");  	return f->get_pascal_string();  }  void File::store_line(const String &p_string) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	f->store_line(p_string);  }  void File::store_csv_line(const Vector<String> &p_values, const String &p_delim) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	f->store_csv_line(p_values, p_delim);  }  void File::store_buffer(const Vector<uint8_t> &p_buffer) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	uint64_t len = p_buffer.size();  	if (len == 0) { @@ -1312,7 +1341,7 @@ bool File::file_exists(const String &p_name) const {  }  void File::store_var(const Variant &p_var, bool p_full_objects) { -	ERR_FAIL_COND_MSG(!f, "File must be opened before use."); +	ERR_FAIL_COND_MSG(!f, "File must be opened before use, or is lacking read-write permission.");  	int len;  	Error err = encode_variant(p_var, nullptr, len, p_full_objects);  	ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant."); @@ -1329,7 +1358,7 @@ void File::store_var(const Variant &p_var, bool p_full_objects) {  }  Variant File::get_var(bool p_allow_objects) const { -	ERR_FAIL_COND_V_MSG(!f, Variant(), "File must be opened before use."); +	ERR_FAIL_COND_V_MSG(!f, Variant(), "File must be opened before use, or is lacking read-write permission.");  	uint32_t len = get_32();  	Vector<uint8_t> buff = get_buffer(len);  	ERR_FAIL_COND_V((uint32_t)buff.size() != len, Variant()); @@ -1441,12 +1470,8 @@ bool Directory::is_open() const {  	return d && dir_open;  } -Error Directory::list_dir_begin(bool p_show_navigational, bool p_show_hidden) { +Error Directory::list_dir_begin() {  	ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); - -	_list_skip_navigational = !p_show_navigational; -	_list_skip_hidden = !p_show_hidden; -  	return d->list_dir_begin();  } @@ -1454,7 +1479,7 @@ String Directory::get_next() {  	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()))) { +	while (!next.is_empty() && ((!include_navigational && (next == "." || next == "..")) || (!include_hidden && d->current_is_hidden()))) {  		next = d->get_next();  	}  	return next; @@ -1470,6 +1495,47 @@ void Directory::list_dir_end() {  	d->list_dir_end();  } +PackedStringArray Directory::get_files() { +	return _get_contents(false); +} + +PackedStringArray Directory::get_directories() { +	return _get_contents(true); +} + +PackedStringArray Directory::_get_contents(bool p_directories) { +	PackedStringArray ret; +	ERR_FAIL_COND_V_MSG(!is_open(), ret, "Directory must be opened before use."); + +	list_dir_begin(); +	String s = get_next(); +	while (!s.is_empty()) { +		if (current_is_dir() == p_directories) { +			ret.append(s); +		} +		s = get_next(); +	} + +	ret.sort(); +	return ret; +} + +void Directory::set_include_navigational(bool p_enable) { +	include_navigational = p_enable; +} + +bool Directory::get_include_navigational() const { +	return include_navigational; +} + +void Directory::set_include_hidden(bool p_enable) { +	include_hidden = p_enable; +} + +bool Directory::get_include_hidden() const { +	return include_hidden; +} +  int Directory::get_drive_count() {  	ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use.");  	return d->get_drive_count(); @@ -1504,7 +1570,7 @@ String Directory::get_current_dir() {  Error Directory::make_dir(String p_dir) {  	ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly."); -	if (!p_dir.is_rel_path()) { +	if (!p_dir.is_relative_path()) {  		DirAccess *d = DirAccess::create_for_path(p_dir);  		Error err = d->make_dir(p_dir);  		memdelete(d); @@ -1515,7 +1581,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 is not configured properly."); -	if (!p_dir.is_rel_path()) { +	if (!p_dir.is_relative_path()) {  		DirAccess *d = DirAccess::create_for_path(p_dir);  		Error err = d->make_dir_recursive(p_dir);  		memdelete(d); @@ -1526,7 +1592,7 @@ Error Directory::make_dir_recursive(String p_dir) {  bool Directory::file_exists(String p_file) {  	ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly."); -	if (!p_file.is_rel_path()) { +	if (!p_file.is_relative_path()) {  		return FileAccess::exists(p_file);  	} @@ -1535,7 +1601,7 @@ bool Directory::file_exists(String p_file) {  bool Directory::dir_exists(String p_dir) {  	ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly."); -	if (!p_dir.is_rel_path()) { +	if (!p_dir.is_relative_path()) {  		DirAccess *d = DirAccess::create_for_path(p_dir);  		bool exists = d->dir_exists(p_dir);  		memdelete(d); @@ -1559,7 +1625,7 @@ Error Directory::rename(String p_from, String p_to) {  	ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use.");  	ERR_FAIL_COND_V_MSG(p_from.is_empty() || p_from == "." || p_from == "..", ERR_INVALID_PARAMETER, "Invalid path to rename."); -	if (!p_from.is_rel_path()) { +	if (!p_from.is_relative_path()) {  		DirAccess *d = DirAccess::create_for_path(p_from);  		ERR_FAIL_COND_V_MSG(!d->file_exists(p_from) && !d->dir_exists(p_from), ERR_DOES_NOT_EXIST, "File or directory does not exist.");  		Error err = d->rename(p_from, p_to); @@ -1573,7 +1639,7 @@ Error Directory::rename(String p_from, String p_to) {  Error Directory::remove(String p_name) {  	ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); -	if (!p_name.is_rel_path()) { +	if (!p_name.is_relative_path()) {  		DirAccess *d = DirAccess::create_for_path(p_name);  		Error err = d->remove(p_name);  		memdelete(d); @@ -1585,10 +1651,12 @@ Error Directory::remove(String p_name) {  void Directory::_bind_methods() {  	ClassDB::bind_method(D_METHOD("open", "path"), &Directory::open); -	ClassDB::bind_method(D_METHOD("list_dir_begin", "show_navigational", "show_hidden"), &Directory::list_dir_begin, DEFVAL(false), DEFVAL(false)); +	ClassDB::bind_method(D_METHOD("list_dir_begin"), &Directory::list_dir_begin, DEFVAL(false), DEFVAL(false));  	ClassDB::bind_method(D_METHOD("get_next"), &Directory::get_next);  	ClassDB::bind_method(D_METHOD("current_is_dir"), &Directory::current_is_dir);  	ClassDB::bind_method(D_METHOD("list_dir_end"), &Directory::list_dir_end); +	ClassDB::bind_method(D_METHOD("get_files"), &Directory::get_files); +	ClassDB::bind_method(D_METHOD("get_directories"), &Directory::get_directories);  	ClassDB::bind_method(D_METHOD("get_drive_count"), &Directory::get_drive_count);  	ClassDB::bind_method(D_METHOD("get_drive", "idx"), &Directory::get_drive);  	ClassDB::bind_method(D_METHOD("get_current_drive"), &Directory::get_current_drive); @@ -1603,6 +1671,14 @@ void Directory::_bind_methods() {  	ClassDB::bind_method(D_METHOD("copy", "from", "to"), &Directory::copy);  	ClassDB::bind_method(D_METHOD("rename", "from", "to"), &Directory::rename);  	ClassDB::bind_method(D_METHOD("remove", "path"), &Directory::remove); + +	ClassDB::bind_method(D_METHOD("set_include_navigational"), &Directory::set_include_navigational); +	ClassDB::bind_method(D_METHOD("get_include_navigational"), &Directory::get_include_navigational); +	ClassDB::bind_method(D_METHOD("set_include_hidden"), &Directory::set_include_hidden); +	ClassDB::bind_method(D_METHOD("get_include_hidden"), &Directory::get_include_hidden); + +	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational"); +	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden");  }  Directory::Directory() { @@ -1636,7 +1712,7 @@ String Marshalls::variant_to_base64(const Variant &p_var, bool p_full_objects) {  	ERR_FAIL_COND_V_MSG(err != OK, "", "Error when trying to encode Variant.");  	String ret = CryptoCore::b64_encode_str(&w[0], len); -	ERR_FAIL_COND_V(ret == "", ret); +	ERR_FAIL_COND_V(ret.is_empty(), ret);  	return ret;  } @@ -1661,7 +1737,7 @@ Variant Marshalls::base64_to_variant(const String &p_str, bool p_allow_objects)  String Marshalls::raw_to_base64(const Vector<uint8_t> &p_arr) {  	String ret = CryptoCore::b64_encode_str(p_arr.ptr(), p_arr.size()); -	ERR_FAIL_COND_V(ret == "", ret); +	ERR_FAIL_COND_V(ret.is_empty(), ret);  	return ret;  } @@ -1685,7 +1761,7 @@ Vector<uint8_t> Marshalls::base64_to_raw(const String &p_str) {  String Marshalls::utf8_to_base64(const String &p_str) {  	CharString cstr = p_str.utf8();  	String ret = CryptoCore::b64_encode_str((unsigned char *)cstr.get_data(), cstr.length()); -	ERR_FAIL_COND_V(ret == "", ret); +	ERR_FAIL_COND_V(ret.is_empty(), ret);  	return ret;  } @@ -1763,6 +1839,13 @@ void Thread::_start_func(void *ud) {  	Ref<Thread> *tud = (Ref<Thread> *)ud;  	Ref<Thread> t = *tud;  	memdelete(tud); + +	Object *target_instance = t->target_callable.get_object(); +	if (!target_instance) { +		t->running.clear(); +		ERR_FAIL_MSG(vformat("Could not call function '%s' on previously freed instance to start thread %s.", t->target_callable.get_method(), t->get_id())); +	} +  	Callable::CallError ce;  	const Variant *arg[1] = { &t->userdata };  	int argc = 0; @@ -1781,58 +1864,43 @@ void Thread::_start_func(void *ud) {  		// We must check if we are in case b).  		int target_param_count = 0;  		int target_default_arg_count = 0; -		Ref<Script> script = t->target_instance->get_script(); +		Ref<Script> script = target_instance->get_script();  		if (script.is_valid()) { -			MethodInfo mi = script->get_method_info(t->target_method); +			MethodInfo mi = script->get_method_info(t->target_callable.get_method());  			target_param_count = mi.arguments.size();  			target_default_arg_count = mi.default_arguments.size();  		} else { -			MethodBind *method = ClassDB::get_method(t->target_instance->get_class_name(), t->target_method); -			target_param_count = method->get_argument_count(); -			target_default_arg_count = method->get_default_argument_count(); +			MethodBind *method = ClassDB::get_method(target_instance->get_class_name(), t->target_callable.get_method()); +			if (method) { +				target_param_count = method->get_argument_count(); +				target_default_arg_count = method->get_default_argument_count(); +			}  		}  		if (target_param_count >= 1 && target_default_arg_count < target_param_count) {  			argc = 1;  		}  	} -	::Thread::set_name(t->target_method); +	::Thread::set_name(t->target_callable.get_method()); -	t->ret = t->target_instance->call(t->target_method, arg, argc, ce); +	t->target_callable.call(arg, argc, t->ret, ce);  	if (ce.error != Callable::CallError::CALL_OK) { -		String reason; -		switch (ce.error) { -			case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: { -				reason = "Invalid Argument #" + itos(ce.argument); -			} break; -			case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: { -				reason = "Too Many Arguments"; -			} break; -			case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: { -				reason = "Too Few Arguments"; -			} break; -			case Callable::CallError::CALL_ERROR_INVALID_METHOD: { -				reason = "Method Not Found"; -			} break; -			default: { -			} -		} - -		ERR_FAIL_MSG("Could not call function '" + t->target_method.operator String() + "' to start thread " + t->get_id() + ": " + reason + "."); +		t->running.clear(); +		ERR_FAIL_MSG("Could not call function '" + t->target_callable.get_method().operator String() + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, arg, argc, ce) + ".");  	} + +	t->running.clear();  } -Error Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) { -	ERR_FAIL_COND_V_MSG(active.is_set(), ERR_ALREADY_IN_USE, "Thread already started."); -	ERR_FAIL_COND_V(!p_instance, ERR_INVALID_PARAMETER); -	ERR_FAIL_COND_V(p_method == StringName(), ERR_INVALID_PARAMETER); +Error Thread::start(const Callable &p_callable, const Variant &p_userdata, Priority p_priority) { +	ERR_FAIL_COND_V_MSG(is_started(), ERR_ALREADY_IN_USE, "Thread already started."); +	ERR_FAIL_COND_V(p_callable.is_null(), ERR_INVALID_PARAMETER);  	ERR_FAIL_INDEX_V(p_priority, PRIORITY_MAX, ERR_INVALID_PARAMETER);  	ret = Variant(); -	target_method = p_method; -	target_instance = p_instance; +	target_callable = p_callable;  	userdata = p_userdata; -	active.set(); +	running.set();  	Ref<Thread> *ud = memnew(Ref<Thread>(this)); @@ -1847,26 +1915,29 @@ String Thread::get_id() const {  	return itos(thread.get_id());  } -bool Thread::is_active() const { -	return active.is_set(); +bool Thread::is_started() const { +	return thread.is_started(); +} + +bool Thread::is_alive() const { +	return running.is_set();  }  Variant Thread::wait_to_finish() { -	ERR_FAIL_COND_V_MSG(!active.is_set(), Variant(), "Thread must be active to wait for its completion."); +	ERR_FAIL_COND_V_MSG(!is_started(), Variant(), "Thread must have been started to wait for its completion.");  	thread.wait_to_finish();  	Variant r = ret; -	active.clear(); -	target_method = StringName(); -	target_instance = nullptr; +	target_callable = Callable();  	userdata = Variant();  	return r;  }  void Thread::_bind_methods() { -	ClassDB::bind_method(D_METHOD("start", "instance", "method", "userdata", "priority"), &Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL)); +	ClassDB::bind_method(D_METHOD("start", "callable", "userdata", "priority"), &Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL));  	ClassDB::bind_method(D_METHOD("get_id"), &Thread::get_id); -	ClassDB::bind_method(D_METHOD("is_active"), &Thread::is_active); +	ClassDB::bind_method(D_METHOD("is_started"), &Thread::is_started); +	ClassDB::bind_method(D_METHOD("is_alive"), &Thread::is_alive);  	ClassDB::bind_method(D_METHOD("wait_to_finish"), &Thread::wait_to_finish);  	BIND_ENUM_CONSTANT(PRIORITY_LOW); @@ -2042,6 +2113,42 @@ StringName ClassDB::get_category(const StringName &p_node) const {  	return ::ClassDB::get_category(p_node);  } +bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const { +	return ::ClassDB::has_enum(p_class, p_name, p_no_inheritance); +} + +PackedStringArray ClassDB::get_enum_list(const StringName &p_class, bool p_no_inheritance) const { +	List<StringName> enums; +	::ClassDB::get_enum_list(p_class, &enums, p_no_inheritance); + +	PackedStringArray ret; +	ret.resize(enums.size()); +	int idx = 0; +	for (const StringName &E : enums) { +		ret.set(idx++, E); +	} + +	return ret; +} + +PackedStringArray ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance) const { +	List<StringName> constants; +	::ClassDB::get_enum_constants(p_class, p_enum, &constants, p_no_inheritance); + +	PackedStringArray ret; +	ret.resize(constants.size()); +	int idx = 0; +	for (const StringName &E : constants) { +		ret.set(idx++, E); +	} + +	return ret; +} + +StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const { +	return ::ClassDB::get_integer_constant_enum(p_class, p_name, p_no_inheritance); +} +  bool ClassDB::is_class_enabled(StringName p_class) const {  	return ::ClassDB::is_class_enabled(p_class);  } @@ -2072,6 +2179,11 @@ void ClassDB::_bind_methods() {  	::ClassDB::bind_method(D_METHOD("class_has_integer_constant", "class", "name"), &ClassDB::has_integer_constant);  	::ClassDB::bind_method(D_METHOD("class_get_integer_constant", "class", "name"), &ClassDB::get_integer_constant); +	::ClassDB::bind_method(D_METHOD("class_has_enum", "class", "name", "no_inheritance"), &ClassDB::has_enum, DEFVAL(false)); +	::ClassDB::bind_method(D_METHOD("class_get_enum_list", "class", "no_inheritance"), &ClassDB::get_enum_list, DEFVAL(false)); +	::ClassDB::bind_method(D_METHOD("class_get_enum_constants", "class", "enum", "no_inheritance"), &ClassDB::get_enum_constants, DEFVAL(false)); +	::ClassDB::bind_method(D_METHOD("class_get_integer_constant_enum", "class", "name", "no_inheritance"), &ClassDB::get_integer_constant_enum, DEFVAL(false)); +  	::ClassDB::bind_method(D_METHOD("class_get_category", "class"), &ClassDB::get_category);  	::ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &ClassDB::is_class_enabled);  } @@ -2175,7 +2287,7 @@ Object *Engine::get_singleton_object(const StringName &p_name) const {  void Engine::register_singleton(const StringName &p_name, Object *p_object) {  	ERR_FAIL_COND_MSG(has_singleton(p_name), "Singleton already registered: " + String(p_name)); -	ERR_FAIL_COND_MSG(p_name.operator String().is_valid_identifier(), "Singleton name is not a valid identifier: " + String(p_name)); +	ERR_FAIL_COND_MSG(!String(p_name).is_valid_identifier(), "Singleton name is not a valid identifier: " + p_name);  	::Engine::Singleton s;  	s.class_name = p_name;  	s.name = p_name; @@ -2251,13 +2363,11 @@ void Engine::_bind_methods() {  	ClassDB::bind_method(D_METHOD("unregister_singleton", "name"), &Engine::unregister_singleton);  	ClassDB::bind_method(D_METHOD("get_singleton_list"), &Engine::get_singleton_list); -	ClassDB::bind_method(D_METHOD("set_editor_hint", "enabled"), &Engine::set_editor_hint);  	ClassDB::bind_method(D_METHOD("is_editor_hint"), &Engine::is_editor_hint);  	ClassDB::bind_method(D_METHOD("set_print_error_messages", "enabled"), &Engine::set_print_error_messages);  	ClassDB::bind_method(D_METHOD("is_printing_error_messages"), &Engine::is_printing_error_messages); -	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_hint"), "set_editor_hint", "is_editor_hint");  	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_error_messages"), "set_print_error_messages", "is_printing_error_messages");  	ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_ticks_per_second"), "set_physics_ticks_per_second", "get_physics_ticks_per_second");  	ADD_PROPERTY(PropertyInfo(Variant::INT, "target_fps"), "set_target_fps", "get_target_fps"); @@ -2388,12 +2498,12 @@ Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Arra  }  EngineDebugger::~EngineDebugger() { -	for (Map<StringName, Callable>::Element *E = captures.front(); E; E = E->next()) { -		::EngineDebugger::unregister_message_capture(E->key()); +	for (const KeyValue<StringName, Callable> &E : captures) { +		::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()); +	for (const KeyValue<StringName, ProfilerCallable> &E : profilers) { +		::EngineDebugger::unregister_profiler(E.key);  	}  	profilers.clear();  } diff --git a/core/core_bind.h b/core/core_bind.h index a6fac63edd..6da4403880 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -119,8 +119,8 @@ protected:  public:  	enum VideoDriver { -		VIDEO_DRIVER_GLES2,  		VIDEO_DRIVER_VULKAN, +		VIDEO_DRIVER_OPENGL_3,  	};  	enum Weekday { @@ -161,10 +161,12 @@ public:  	int get_low_processor_usage_mode_sleep_usec() const;  	void alert(const String &p_alert, const String &p_title = "ALERT!"); +	void crash(const String &p_message);  	String get_executable_path() const;  	int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false);  	int create_process(const String &p_path, const Vector<String> &p_arguments); +	int create_instance(const Vector<String> &p_arguments);  	Error kill(int p_pid);  	Error shell_open(String p_uri); @@ -178,6 +180,7 @@ public:  	Vector<String> get_cmdline_args();  	String get_locale() const; +	String get_locale_language() const;  	String get_model_name() const; @@ -193,9 +196,9 @@ public:  	String get_unique_id() const; -	String get_keycode_string(uint32_t p_code) const; -	bool is_keycode_unicode(uint32_t p_unicode) const; -	int find_keycode_from_string(const String &p_code) const; +	String get_keycode_string(Key p_code) const; +	bool is_keycode_unicode(char32_t p_unicode) const; +	Key find_keycode_from_string(const String &p_code) const;  	void set_use_file_access_save_and_swap(bool p_enable); @@ -204,7 +207,7 @@ public:  	void delay_usec(int p_usec) const;  	void delay_msec(int p_msec) const; -	uint32_t get_ticks_msec() const; +	uint64_t get_ticks_msec() const;  	uint64_t get_ticks_usec() const;  	bool can_use_threads() const; @@ -235,6 +238,7 @@ public:  	Error set_thread_name(const String &p_name);  	Thread::ID get_thread_caller_id() const; +	Thread::ID get_main_thread_id() const;  	bool has_feature(const String &p_feature) const; @@ -440,7 +444,10 @@ public:  class Directory : public RefCounted {  	GDCLASS(Directory, RefCounted);  	DirAccess *d; +  	bool dir_open = false; +	bool include_navigational = false; +	bool include_hidden = false;  protected:  	static void _bind_methods(); @@ -450,12 +457,20 @@ public:  	bool is_open() const; -	Error list_dir_begin(bool p_show_navigational = false, bool p_show_hidden = false); // This starts dir listing. +	Error list_dir_begin();  	String get_next();  	bool current_is_dir() const; -  	void list_dir_end(); +	PackedStringArray get_files(); +	PackedStringArray get_directories(); +	PackedStringArray _get_contents(bool p_directories); + +	void set_include_navigational(bool p_enable); +	bool get_include_navigational() const; +	void set_include_hidden(bool p_enable); +	bool get_include_hidden() const; +  	int get_drive_count();  	String get_drive(int p_drive);  	int get_current_drive(); @@ -477,10 +492,6 @@ public:  	Directory();  	virtual ~Directory(); - -private: -	bool _list_skip_navigational = false; -	bool _list_skip_hidden = false;  };  class Marshalls : public Object { @@ -537,9 +548,8 @@ class Thread : public RefCounted {  protected:  	Variant ret;  	Variant userdata; -	SafeFlag active; -	Object *target_instance = nullptr; -	StringName target_method; +	SafeFlag running; +	Callable target_callable;  	::Thread thread;  	static void _bind_methods();  	static void _start_func(void *ud); @@ -552,9 +562,10 @@ public:  		PRIORITY_MAX  	}; -	Error start(Object *p_instance, const StringName &p_method, const Variant &p_userdata = Variant(), Priority p_priority = PRIORITY_NORMAL); +	Error start(const Callable &p_callable, const Variant &p_userdata = Variant(), Priority p_priority = PRIORITY_NORMAL);  	String get_id() const; -	bool is_active() const; +	bool is_started() const; +	bool is_alive() const;  	Variant wait_to_finish();  }; @@ -592,6 +603,11 @@ public:  	int get_integer_constant(const StringName &p_class, const StringName &p_name) const;  	StringName get_category(const StringName &p_node) const; +	bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; +	PackedStringArray get_enum_list(const StringName &p_class, bool p_no_inheritance = false) const; +	PackedStringArray get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const; +	StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; +  	bool is_class_enabled(StringName p_class) const;  	ClassDB() {} diff --git a/core/core_builders.py b/core/core_builders.py index 004475faa7..b07daa80ae 100644 --- a/core/core_builders.py +++ b/core/core_builders.py @@ -35,7 +35,9 @@ def make_certs_header(target, source, env):      decomp_size = len(buf)      import zlib -    buf = zlib.compress(buf) +    # Use maximum zlib compression level to further reduce file size +    # (at the cost of initial build times). +    buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)      g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")      g.write("#ifndef CERTS_COMPRESSED_GEN_H\n") diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 4f3f1fd16e..51fcfd5305 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -31,6 +31,7 @@  #include "core_constants.h"  #include "core/input/input_event.h" +#include "core/multiplayer/multiplayer.h"  #include "core/object/class_db.h"  #include "core/os/keyboard.h"  #include "core/variant/variant.h" @@ -70,6 +71,16 @@ static Vector<_CoreConstant> _global_constants;  #define BIND_CORE_ENUM_CONSTANT(m_constant) \  	_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant)); +// This just binds enum classes as if they were regular enum constants. +#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \ +	_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member), #m_prefix "_" #m_member, (int)m_enum::m_member)); + +#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \ +	_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_enum::m_member, #m_name), #m_name, (int)m_enum::m_member)); + +#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \ +	_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member), #m_prefix "_" #m_member, (int)m_enum::m_member, true)); +  #define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \  	_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_constant, #m_constant), m_custom_name, m_constant)); @@ -90,6 +101,16 @@ static Vector<_CoreConstant> _global_constants;  #define BIND_CORE_ENUM_CONSTANT(m_constant) \  	_global_constants.push_back(_CoreConstant(#m_constant, m_constant)); +// This just binds enum classes as if they were regular enum constants. +#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \ +	_global_constants.push_back(_CoreConstant(#m_prefix "_" #m_member, (int)m_enum::m_member)); + +#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \ +	_global_constants.push_back(_CoreConstant(#m_name, (int)m_enum::m_member)); + +#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \ +	_global_constants.push_back(_CoreConstant(#m_prefix "_" #m_member, (int)m_enum::m_member)); +  #define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \  	_global_constants.push_back(_CoreConstant(m_custom_name, m_constant)); @@ -121,348 +142,340 @@ void register_global_constants() {  	BIND_CORE_ENUM_CONSTANT(CLOCKWISE);  	BIND_CORE_ENUM_CONSTANT(COUNTERCLOCKWISE); -	BIND_CORE_ENUM_CONSTANT(HALIGN_LEFT); -	BIND_CORE_ENUM_CONSTANT(HALIGN_CENTER); -	BIND_CORE_ENUM_CONSTANT(HALIGN_RIGHT); -	BIND_CORE_ENUM_CONSTANT(HALIGN_FILL); - -	BIND_CORE_ENUM_CONSTANT(VALIGN_TOP); -	BIND_CORE_ENUM_CONSTANT(VALIGN_CENTER); -	BIND_CORE_ENUM_CONSTANT(VALIGN_BOTTOM); - -	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TOP_TO); -	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_CENTER_TO); -	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_BOTTOM_TO); - -	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_TOP); -	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_CENTER); -	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_BASELINE); -	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_BOTTOM); - -	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TOP); -	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_CENTER); -	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_BOTTOM); - -	// huge list of keys -	BIND_CORE_CONSTANT(SPKEY); - -	BIND_CORE_ENUM_CONSTANT(KEY_ESCAPE); -	BIND_CORE_ENUM_CONSTANT(KEY_TAB); -	BIND_CORE_ENUM_CONSTANT(KEY_BACKTAB); -	BIND_CORE_ENUM_CONSTANT(KEY_BACKSPACE); -	BIND_CORE_ENUM_CONSTANT(KEY_ENTER); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_ENTER); -	BIND_CORE_ENUM_CONSTANT(KEY_INSERT); -	BIND_CORE_ENUM_CONSTANT(KEY_DELETE); -	BIND_CORE_ENUM_CONSTANT(KEY_PAUSE); -	BIND_CORE_ENUM_CONSTANT(KEY_PRINT); -	BIND_CORE_ENUM_CONSTANT(KEY_SYSREQ); -	BIND_CORE_ENUM_CONSTANT(KEY_CLEAR); -	BIND_CORE_ENUM_CONSTANT(KEY_HOME); -	BIND_CORE_ENUM_CONSTANT(KEY_END); -	BIND_CORE_ENUM_CONSTANT(KEY_LEFT); -	BIND_CORE_ENUM_CONSTANT(KEY_UP); -	BIND_CORE_ENUM_CONSTANT(KEY_RIGHT); -	BIND_CORE_ENUM_CONSTANT(KEY_DOWN); -	BIND_CORE_ENUM_CONSTANT(KEY_PAGEUP); -	BIND_CORE_ENUM_CONSTANT(KEY_PAGEDOWN); -	BIND_CORE_ENUM_CONSTANT(KEY_SHIFT); -	BIND_CORE_ENUM_CONSTANT(KEY_CTRL); -	BIND_CORE_ENUM_CONSTANT(KEY_META); -	BIND_CORE_ENUM_CONSTANT(KEY_ALT); -	BIND_CORE_ENUM_CONSTANT(KEY_CAPSLOCK); -	BIND_CORE_ENUM_CONSTANT(KEY_NUMLOCK); -	BIND_CORE_ENUM_CONSTANT(KEY_SCROLLLOCK); -	BIND_CORE_ENUM_CONSTANT(KEY_F1); -	BIND_CORE_ENUM_CONSTANT(KEY_F2); -	BIND_CORE_ENUM_CONSTANT(KEY_F3); -	BIND_CORE_ENUM_CONSTANT(KEY_F4); -	BIND_CORE_ENUM_CONSTANT(KEY_F5); -	BIND_CORE_ENUM_CONSTANT(KEY_F6); -	BIND_CORE_ENUM_CONSTANT(KEY_F7); -	BIND_CORE_ENUM_CONSTANT(KEY_F8); -	BIND_CORE_ENUM_CONSTANT(KEY_F9); -	BIND_CORE_ENUM_CONSTANT(KEY_F10); -	BIND_CORE_ENUM_CONSTANT(KEY_F11); -	BIND_CORE_ENUM_CONSTANT(KEY_F12); -	BIND_CORE_ENUM_CONSTANT(KEY_F13); -	BIND_CORE_ENUM_CONSTANT(KEY_F14); -	BIND_CORE_ENUM_CONSTANT(KEY_F15); -	BIND_CORE_ENUM_CONSTANT(KEY_F16); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_MULTIPLY); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_DIVIDE); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_SUBTRACT); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_PERIOD); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_ADD); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_0); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_1); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_2); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_3); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_4); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_5); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_6); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_7); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_8); -	BIND_CORE_ENUM_CONSTANT(KEY_KP_9); -	BIND_CORE_ENUM_CONSTANT(KEY_SUPER_L); -	BIND_CORE_ENUM_CONSTANT(KEY_SUPER_R); -	BIND_CORE_ENUM_CONSTANT(KEY_MENU); -	BIND_CORE_ENUM_CONSTANT(KEY_HYPER_L); -	BIND_CORE_ENUM_CONSTANT(KEY_HYPER_R); -	BIND_CORE_ENUM_CONSTANT(KEY_HELP); -	BIND_CORE_ENUM_CONSTANT(KEY_DIRECTION_L); -	BIND_CORE_ENUM_CONSTANT(KEY_DIRECTION_R); -	BIND_CORE_ENUM_CONSTANT(KEY_BACK); -	BIND_CORE_ENUM_CONSTANT(KEY_FORWARD); -	BIND_CORE_ENUM_CONSTANT(KEY_STOP); -	BIND_CORE_ENUM_CONSTANT(KEY_REFRESH); -	BIND_CORE_ENUM_CONSTANT(KEY_VOLUMEDOWN); -	BIND_CORE_ENUM_CONSTANT(KEY_VOLUMEMUTE); -	BIND_CORE_ENUM_CONSTANT(KEY_VOLUMEUP); -	BIND_CORE_ENUM_CONSTANT(KEY_BASSBOOST); -	BIND_CORE_ENUM_CONSTANT(KEY_BASSUP); -	BIND_CORE_ENUM_CONSTANT(KEY_BASSDOWN); -	BIND_CORE_ENUM_CONSTANT(KEY_TREBLEUP); -	BIND_CORE_ENUM_CONSTANT(KEY_TREBLEDOWN); -	BIND_CORE_ENUM_CONSTANT(KEY_MEDIAPLAY); -	BIND_CORE_ENUM_CONSTANT(KEY_MEDIASTOP); -	BIND_CORE_ENUM_CONSTANT(KEY_MEDIAPREVIOUS); -	BIND_CORE_ENUM_CONSTANT(KEY_MEDIANEXT); -	BIND_CORE_ENUM_CONSTANT(KEY_MEDIARECORD); -	BIND_CORE_ENUM_CONSTANT(KEY_HOMEPAGE); -	BIND_CORE_ENUM_CONSTANT(KEY_FAVORITES); -	BIND_CORE_ENUM_CONSTANT(KEY_SEARCH); -	BIND_CORE_ENUM_CONSTANT(KEY_STANDBY); -	BIND_CORE_ENUM_CONSTANT(KEY_OPENURL); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHMAIL); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHMEDIA); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH0); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH1); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH2); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH3); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH4); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH5); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH6); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH7); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH8); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCH9); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHA); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHB); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHC); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHD); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHE); -	BIND_CORE_ENUM_CONSTANT(KEY_LAUNCHF); - -	BIND_CORE_ENUM_CONSTANT(KEY_UNKNOWN); -	BIND_CORE_ENUM_CONSTANT(KEY_SPACE); -	BIND_CORE_ENUM_CONSTANT(KEY_EXCLAM); -	BIND_CORE_ENUM_CONSTANT(KEY_QUOTEDBL); -	BIND_CORE_ENUM_CONSTANT(KEY_NUMBERSIGN); -	BIND_CORE_ENUM_CONSTANT(KEY_DOLLAR); -	BIND_CORE_ENUM_CONSTANT(KEY_PERCENT); -	BIND_CORE_ENUM_CONSTANT(KEY_AMPERSAND); -	BIND_CORE_ENUM_CONSTANT(KEY_APOSTROPHE); -	BIND_CORE_ENUM_CONSTANT(KEY_PARENLEFT); -	BIND_CORE_ENUM_CONSTANT(KEY_PARENRIGHT); -	BIND_CORE_ENUM_CONSTANT(KEY_ASTERISK); -	BIND_CORE_ENUM_CONSTANT(KEY_PLUS); -	BIND_CORE_ENUM_CONSTANT(KEY_COMMA); -	BIND_CORE_ENUM_CONSTANT(KEY_MINUS); -	BIND_CORE_ENUM_CONSTANT(KEY_PERIOD); -	BIND_CORE_ENUM_CONSTANT(KEY_SLASH); -	BIND_CORE_ENUM_CONSTANT(KEY_0); -	BIND_CORE_ENUM_CONSTANT(KEY_1); -	BIND_CORE_ENUM_CONSTANT(KEY_2); -	BIND_CORE_ENUM_CONSTANT(KEY_3); -	BIND_CORE_ENUM_CONSTANT(KEY_4); -	BIND_CORE_ENUM_CONSTANT(KEY_5); -	BIND_CORE_ENUM_CONSTANT(KEY_6); -	BIND_CORE_ENUM_CONSTANT(KEY_7); -	BIND_CORE_ENUM_CONSTANT(KEY_8); -	BIND_CORE_ENUM_CONSTANT(KEY_9); -	BIND_CORE_ENUM_CONSTANT(KEY_COLON); -	BIND_CORE_ENUM_CONSTANT(KEY_SEMICOLON); -	BIND_CORE_ENUM_CONSTANT(KEY_LESS); -	BIND_CORE_ENUM_CONSTANT(KEY_EQUAL); -	BIND_CORE_ENUM_CONSTANT(KEY_GREATER); -	BIND_CORE_ENUM_CONSTANT(KEY_QUESTION); -	BIND_CORE_ENUM_CONSTANT(KEY_AT); -	BIND_CORE_ENUM_CONSTANT(KEY_A); -	BIND_CORE_ENUM_CONSTANT(KEY_B); -	BIND_CORE_ENUM_CONSTANT(KEY_C); -	BIND_CORE_ENUM_CONSTANT(KEY_D); -	BIND_CORE_ENUM_CONSTANT(KEY_E); -	BIND_CORE_ENUM_CONSTANT(KEY_F); -	BIND_CORE_ENUM_CONSTANT(KEY_G); -	BIND_CORE_ENUM_CONSTANT(KEY_H); -	BIND_CORE_ENUM_CONSTANT(KEY_I); -	BIND_CORE_ENUM_CONSTANT(KEY_J); -	BIND_CORE_ENUM_CONSTANT(KEY_K); -	BIND_CORE_ENUM_CONSTANT(KEY_L); -	BIND_CORE_ENUM_CONSTANT(KEY_M); -	BIND_CORE_ENUM_CONSTANT(KEY_N); -	BIND_CORE_ENUM_CONSTANT(KEY_O); -	BIND_CORE_ENUM_CONSTANT(KEY_P); -	BIND_CORE_ENUM_CONSTANT(KEY_Q); -	BIND_CORE_ENUM_CONSTANT(KEY_R); -	BIND_CORE_ENUM_CONSTANT(KEY_S); -	BIND_CORE_ENUM_CONSTANT(KEY_T); -	BIND_CORE_ENUM_CONSTANT(KEY_U); -	BIND_CORE_ENUM_CONSTANT(KEY_V); -	BIND_CORE_ENUM_CONSTANT(KEY_W); -	BIND_CORE_ENUM_CONSTANT(KEY_X); -	BIND_CORE_ENUM_CONSTANT(KEY_Y); -	BIND_CORE_ENUM_CONSTANT(KEY_Z); -	BIND_CORE_ENUM_CONSTANT(KEY_BRACKETLEFT); -	BIND_CORE_ENUM_CONSTANT(KEY_BACKSLASH); -	BIND_CORE_ENUM_CONSTANT(KEY_BRACKETRIGHT); -	BIND_CORE_ENUM_CONSTANT(KEY_ASCIICIRCUM); -	BIND_CORE_ENUM_CONSTANT(KEY_UNDERSCORE); -	BIND_CORE_ENUM_CONSTANT(KEY_QUOTELEFT); -	BIND_CORE_ENUM_CONSTANT(KEY_BRACELEFT); -	BIND_CORE_ENUM_CONSTANT(KEY_BAR); -	BIND_CORE_ENUM_CONSTANT(KEY_BRACERIGHT); -	BIND_CORE_ENUM_CONSTANT(KEY_ASCIITILDE); -	BIND_CORE_ENUM_CONSTANT(KEY_NOBREAKSPACE); -	BIND_CORE_ENUM_CONSTANT(KEY_EXCLAMDOWN); -	BIND_CORE_ENUM_CONSTANT(KEY_CENT); -	BIND_CORE_ENUM_CONSTANT(KEY_STERLING); -	BIND_CORE_ENUM_CONSTANT(KEY_CURRENCY); -	BIND_CORE_ENUM_CONSTANT(KEY_YEN); -	BIND_CORE_ENUM_CONSTANT(KEY_BROKENBAR); -	BIND_CORE_ENUM_CONSTANT(KEY_SECTION); -	BIND_CORE_ENUM_CONSTANT(KEY_DIAERESIS); -	BIND_CORE_ENUM_CONSTANT(KEY_COPYRIGHT); -	BIND_CORE_ENUM_CONSTANT(KEY_ORDFEMININE); -	BIND_CORE_ENUM_CONSTANT(KEY_GUILLEMOTLEFT); -	BIND_CORE_ENUM_CONSTANT(KEY_NOTSIGN); -	BIND_CORE_ENUM_CONSTANT(KEY_HYPHEN); -	BIND_CORE_ENUM_CONSTANT(KEY_REGISTERED); -	BIND_CORE_ENUM_CONSTANT(KEY_MACRON); -	BIND_CORE_ENUM_CONSTANT(KEY_DEGREE); -	BIND_CORE_ENUM_CONSTANT(KEY_PLUSMINUS); -	BIND_CORE_ENUM_CONSTANT(KEY_TWOSUPERIOR); -	BIND_CORE_ENUM_CONSTANT(KEY_THREESUPERIOR); -	BIND_CORE_ENUM_CONSTANT(KEY_ACUTE); -	BIND_CORE_ENUM_CONSTANT(KEY_MU); -	BIND_CORE_ENUM_CONSTANT(KEY_PARAGRAPH); -	BIND_CORE_ENUM_CONSTANT(KEY_PERIODCENTERED); -	BIND_CORE_ENUM_CONSTANT(KEY_CEDILLA); -	BIND_CORE_ENUM_CONSTANT(KEY_ONESUPERIOR); -	BIND_CORE_ENUM_CONSTANT(KEY_MASCULINE); -	BIND_CORE_ENUM_CONSTANT(KEY_GUILLEMOTRIGHT); -	BIND_CORE_ENUM_CONSTANT(KEY_ONEQUARTER); -	BIND_CORE_ENUM_CONSTANT(KEY_ONEHALF); -	BIND_CORE_ENUM_CONSTANT(KEY_THREEQUARTERS); -	BIND_CORE_ENUM_CONSTANT(KEY_QUESTIONDOWN); -	BIND_CORE_ENUM_CONSTANT(KEY_AGRAVE); -	BIND_CORE_ENUM_CONSTANT(KEY_AACUTE); -	BIND_CORE_ENUM_CONSTANT(KEY_ACIRCUMFLEX); -	BIND_CORE_ENUM_CONSTANT(KEY_ATILDE); -	BIND_CORE_ENUM_CONSTANT(KEY_ADIAERESIS); -	BIND_CORE_ENUM_CONSTANT(KEY_ARING); -	BIND_CORE_ENUM_CONSTANT(KEY_AE); -	BIND_CORE_ENUM_CONSTANT(KEY_CCEDILLA); -	BIND_CORE_ENUM_CONSTANT(KEY_EGRAVE); -	BIND_CORE_ENUM_CONSTANT(KEY_EACUTE); -	BIND_CORE_ENUM_CONSTANT(KEY_ECIRCUMFLEX); -	BIND_CORE_ENUM_CONSTANT(KEY_EDIAERESIS); -	BIND_CORE_ENUM_CONSTANT(KEY_IGRAVE); -	BIND_CORE_ENUM_CONSTANT(KEY_IACUTE); -	BIND_CORE_ENUM_CONSTANT(KEY_ICIRCUMFLEX); -	BIND_CORE_ENUM_CONSTANT(KEY_IDIAERESIS); -	BIND_CORE_ENUM_CONSTANT(KEY_ETH); -	BIND_CORE_ENUM_CONSTANT(KEY_NTILDE); -	BIND_CORE_ENUM_CONSTANT(KEY_OGRAVE); -	BIND_CORE_ENUM_CONSTANT(KEY_OACUTE); -	BIND_CORE_ENUM_CONSTANT(KEY_OCIRCUMFLEX); -	BIND_CORE_ENUM_CONSTANT(KEY_OTILDE); -	BIND_CORE_ENUM_CONSTANT(KEY_ODIAERESIS); -	BIND_CORE_ENUM_CONSTANT(KEY_MULTIPLY); -	BIND_CORE_ENUM_CONSTANT(KEY_OOBLIQUE); -	BIND_CORE_ENUM_CONSTANT(KEY_UGRAVE); -	BIND_CORE_ENUM_CONSTANT(KEY_UACUTE); -	BIND_CORE_ENUM_CONSTANT(KEY_UCIRCUMFLEX); -	BIND_CORE_ENUM_CONSTANT(KEY_UDIAERESIS); -	BIND_CORE_ENUM_CONSTANT(KEY_YACUTE); -	BIND_CORE_ENUM_CONSTANT(KEY_THORN); -	BIND_CORE_ENUM_CONSTANT(KEY_SSHARP); - -	BIND_CORE_ENUM_CONSTANT(KEY_DIVISION); -	BIND_CORE_ENUM_CONSTANT(KEY_YDIAERESIS); - -	BIND_CORE_ENUM_CONSTANT(KEY_CODE_MASK); -	BIND_CORE_ENUM_CONSTANT(KEY_MODIFIER_MASK); - -	BIND_CORE_ENUM_CONSTANT(KEY_MASK_SHIFT); -	BIND_CORE_ENUM_CONSTANT(KEY_MASK_ALT); -	BIND_CORE_ENUM_CONSTANT(KEY_MASK_META); -	BIND_CORE_ENUM_CONSTANT(KEY_MASK_CTRL); -	BIND_CORE_ENUM_CONSTANT_NO_VAL(KEY_MASK_CMD); -	BIND_CORE_ENUM_CONSTANT(KEY_MASK_KPAD); -	BIND_CORE_ENUM_CONSTANT(KEY_MASK_GROUP_SWITCH); - -	// mouse -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_LEFT); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_RIGHT); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_MIDDLE); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_XBUTTON1); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_XBUTTON2); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_WHEEL_UP); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_WHEEL_DOWN); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_WHEEL_LEFT); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_WHEEL_RIGHT); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_MASK_LEFT); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_MASK_RIGHT); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_MASK_MIDDLE); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_MASK_XBUTTON1); -	BIND_CORE_ENUM_CONSTANT(MOUSE_BUTTON_MASK_XBUTTON2); - -	// Joypad buttons -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_INVALID); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_A); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_B); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_X); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_Y); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_BACK); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_GUIDE); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_START); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_LEFT_STICK); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_RIGHT_STICK); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_LEFT_SHOULDER); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_RIGHT_SHOULDER); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_DPAD_UP); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_DPAD_DOWN); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_DPAD_LEFT); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_DPAD_RIGHT); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_MISC1); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_PADDLE1); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_PADDLE2); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_PADDLE3); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_PADDLE4); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_TOUCHPAD); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_SDL_MAX); -	BIND_CORE_ENUM_CONSTANT(JOY_BUTTON_MAX); - -	// Joypad axes -	BIND_CORE_ENUM_CONSTANT(JOY_AXIS_INVALID); -	BIND_CORE_ENUM_CONSTANT(JOY_AXIS_LEFT_X); -	BIND_CORE_ENUM_CONSTANT(JOY_AXIS_LEFT_Y); -	BIND_CORE_ENUM_CONSTANT(JOY_AXIS_RIGHT_X); -	BIND_CORE_ENUM_CONSTANT(JOY_AXIS_RIGHT_Y); -	BIND_CORE_ENUM_CONSTANT(JOY_AXIS_TRIGGER_LEFT); -	BIND_CORE_ENUM_CONSTANT(JOY_AXIS_TRIGGER_RIGHT); -	BIND_CORE_ENUM_CONSTANT(JOY_AXIS_SDL_MAX); -	BIND_CORE_ENUM_CONSTANT(JOY_AXIS_MAX); - -	// midi -	BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_OFF); -	BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_ON); -	BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_AFTERTOUCH); -	BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_CONTROL_CHANGE); -	BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_PROGRAM_CHANGE); -	BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_CHANNEL_PRESSURE); -	BIND_CORE_ENUM_CONSTANT(MIDI_MESSAGE_PITCH_BEND); +	BIND_CORE_ENUM_CONSTANT(HORIZONTAL_ALIGNMENT_LEFT); +	BIND_CORE_ENUM_CONSTANT(HORIZONTAL_ALIGNMENT_CENTER); +	BIND_CORE_ENUM_CONSTANT(HORIZONTAL_ALIGNMENT_RIGHT); +	BIND_CORE_ENUM_CONSTANT(HORIZONTAL_ALIGNMENT_FILL); + +	BIND_CORE_ENUM_CONSTANT(VERTICAL_ALIGNMENT_TOP); +	BIND_CORE_ENUM_CONSTANT(VERTICAL_ALIGNMENT_CENTER); +	BIND_CORE_ENUM_CONSTANT(VERTICAL_ALIGNMENT_BOTTOM); +	BIND_CORE_ENUM_CONSTANT(VERTICAL_ALIGNMENT_FILL); + +	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TOP_TO); +	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_CENTER_TO); +	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_BOTTOM_TO); + +	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TO_TOP); +	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TO_CENTER); +	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TO_BASELINE); +	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TO_BOTTOM); + +	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TOP); +	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_CENTER); +	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_BOTTOM); + +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SPECIAL); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ESCAPE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, TAB); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BACKTAB); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BACKSPACE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ENTER); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_ENTER); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, INSERT); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_DELETE, KEY_DELETE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PAUSE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PRINT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SYSREQ); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, CLEAR); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, HOME); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, END); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LEFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UP); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, RIGHT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, DOWN); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PAGEUP); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PAGEDOWN); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SHIFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, CTRL); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, META); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ALT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, CAPSLOCK); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, NUMLOCK); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SCROLLLOCK); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F1); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F2); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F3); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F4); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F5); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F6); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F7); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F8); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F9); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F10); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F11); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F12); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F13); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F14); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F15); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F16); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_MULTIPLY); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_DIVIDE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_SUBTRACT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_PERIOD); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_ADD); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_0); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_1); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_2); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_3); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_4); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_5); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_6); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_7); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_8); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_9); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SUPER_L); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SUPER_R); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MENU); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, HYPER_L); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, HYPER_R); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, HELP); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, DIRECTION_L); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, DIRECTION_R); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BACK); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, FORWARD); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, STOP); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, REFRESH); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, VOLUMEDOWN); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, VOLUMEMUTE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, VOLUMEUP); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BASSBOOST); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BASSUP); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BASSDOWN); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, TREBLEUP); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, TREBLEDOWN); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MEDIAPLAY); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MEDIASTOP); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MEDIAPREVIOUS); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MEDIANEXT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MEDIARECORD); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, HOMEPAGE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, FAVORITES); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SEARCH); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, STANDBY); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, OPENURL); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHMAIL); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHMEDIA); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH0); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH1); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH2); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH3); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH4); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH5); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH6); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH7); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH8); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH9); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHA); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHB); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHC); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHD); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHF); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UNKNOWN); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SPACE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, EXCLAM); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, QUOTEDBL); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, NUMBERSIGN); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, DOLLAR); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PERCENT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, AMPERSAND); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, APOSTROPHE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PARENLEFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PARENRIGHT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ASTERISK); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PLUS); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, COMMA); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MINUS); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PERIOD); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SLASH); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_0, KEY_0); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_1, KEY_1); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_2, KEY_2); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_3, KEY_3); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_4, KEY_4); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_5, KEY_5); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_6, KEY_6); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_7, KEY_7); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_8, KEY_8); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_9, KEY_9); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, COLON); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SEMICOLON); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LESS); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, EQUAL); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, GREATER); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, QUESTION); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, AT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, A); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, B); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, C); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, D); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, E); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, G); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, H); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, I); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, J); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, K); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, L); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, M); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, N); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, O); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, P); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, Q); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, R); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, S); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, T); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, U); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, V); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, W); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, X); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, Y); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, Z); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BRACKETLEFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BACKSLASH); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BRACKETRIGHT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ASCIICIRCUM); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UNDERSCORE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, QUOTELEFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BRACELEFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BAR); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BRACERIGHT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ASCIITILDE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, NOBREAKSPACE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, EXCLAMDOWN); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, CENT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, STERLING); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, CURRENCY); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, YEN); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BROKENBAR); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SECTION); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, DIAERESIS); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, COPYRIGHT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ORDFEMININE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, GUILLEMOTLEFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, NOTSIGN); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, HYPHEN); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_REGISTERED, KEY_REGISTERED); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MACRON); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, DEGREE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PLUSMINUS); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, TWOSUPERIOR); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, THREESUPERIOR); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ACUTE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MU); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PARAGRAPH); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PERIODCENTERED); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, CEDILLA); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ONESUPERIOR); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MASCULINE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, GUILLEMOTRIGHT); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ONEQUARTER); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ONEHALF); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, THREEQUARTERS); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, QUESTIONDOWN); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, AGRAVE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, AACUTE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ACIRCUMFLEX); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ATILDE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ADIAERESIS); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ARING); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, AE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, CCEDILLA); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, EGRAVE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, EACUTE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ECIRCUMFLEX); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, EDIAERESIS); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, IGRAVE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, IACUTE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ICIRCUMFLEX); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, IDIAERESIS); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ETH); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, NTILDE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, OGRAVE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, OACUTE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, OCIRCUMFLEX); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, OTILDE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ODIAERESIS); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MULTIPLY); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, OOBLIQUE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UGRAVE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UACUTE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UCIRCUMFLEX); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UDIAERESIS); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, YACUTE); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, THORN); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SSHARP); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, DIVISION); +	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, YDIAERESIS); + +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(KeyModifierMask, KEY_CODE_MASK, CODE_MASK); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(KeyModifierMask, KEY_MODIFIER_MASK, MODIFIER_MASK); +	BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, SHIFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, ALT); +	BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, META); +	BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, CTRL); +	BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(KeyModifierMask, KEY_MASK, CMD); +	BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, KPAD); +	BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, GROUP_SWITCH); + +	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, LEFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, RIGHT); +	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MIDDLE); +	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, WHEEL_UP); +	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, WHEEL_DOWN); +	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, WHEEL_LEFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, WHEEL_RIGHT); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(MouseButton, MOUSE_BUTTON_XBUTTON1, MB_XBUTTON1); +	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(MouseButton, MOUSE_BUTTON_XBUTTON2, MB_XBUTTON2); +	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MASK_LEFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MASK_RIGHT); +	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MASK_MIDDLE); +	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MASK_XBUTTON1); +	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MASK_XBUTTON2); + +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, INVALID); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, A); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, B); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, X); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, Y); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, BACK); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, GUIDE); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, START); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, LEFT_STICK); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, RIGHT_STICK); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, LEFT_SHOULDER); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, RIGHT_SHOULDER); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, DPAD_UP); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, DPAD_DOWN); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, DPAD_LEFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, DPAD_RIGHT); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MISC1); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE1); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE2); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE3); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE4); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, TOUCHPAD); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, SDL_MAX); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MAX); + +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, INVALID); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, LEFT_X); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, LEFT_Y); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, RIGHT_X); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, RIGHT_Y); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, TRIGGER_LEFT); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, TRIGGER_RIGHT); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, SDL_MAX); +	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, MAX); + +	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NOTE_OFF); +	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NOTE_ON); +	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, AFTERTOUCH); +	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, CONTROL_CHANGE); +	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, PROGRAM_CHANGE); +	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, CHANNEL_PRESSURE); +	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, PITCH_BEND);  	// error list @@ -593,10 +606,11 @@ void register_global_constants() {  	BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFERRED_SET_RESOURCE);  	BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT);  	BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR_BASIC_SETTING); +	BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_ARRAY);  	BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT);  	BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT_INTL); -	BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NOEDITOR); +	BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NO_EDITOR);  	BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_NORMAL);  	BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_EDITOR); @@ -609,6 +623,15 @@ void register_global_constants() {  	BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_OBJECT_CORE);  	BIND_CORE_ENUM_CONSTANT(METHOD_FLAGS_DEFAULT); +	// rpc +	BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_DISABLED", Multiplayer::RPC_MODE_DISABLED); +	BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_ANY_PEER", Multiplayer::RPC_MODE_ANY_PEER); +	BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_AUTH", Multiplayer::RPC_MODE_AUTHORITY); + +	BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_UNRELIABLE", Multiplayer::TRANSFER_MODE_UNRELIABLE); +	BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_UNRELIABLE_ORDERED", Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED); +	BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_RELIABLE", Multiplayer::TRANSFER_MODE_RELIABLE); +  	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NIL", Variant::NIL);  	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BOOL", Variant::BOOL);  	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_INT", Variant::INT); diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp index a522b1310f..059025aa8f 100644 --- a/core/debugger/engine_debugger.cpp +++ b/core/debugger/engine_debugger.cpp @@ -123,8 +123,8 @@ void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks,  	physics_time = USEC_TO_SEC(p_physics_ticks);  	physics_frame_time = p_physics_frame_time;  	// Notify tick to running profilers -	for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) { -		Profiler &p = E->get(); +	for (KeyValue<StringName, Profiler> &E : profilers) { +		Profiler &p = E.value;  		if (!p.active || !p.tick) {  			continue;  		} @@ -179,9 +179,9 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, Ve  void EngineDebugger::deinitialize() {  	if (singleton) {  		// Stop all profilers -		for (Map<StringName, Profiler>::Element *E = profilers.front(); E; E = E->next()) { -			if (E->get().active) { -				singleton->profiler_enable(E->key(), false); +		for (const KeyValue<StringName, Profiler> &E : profilers) { +			if (E.value.active) { +				singleton->profiler_enable(E.key, false);  			}  		} diff --git a/core/debugger/engine_debugger.h b/core/debugger/engine_debugger.h index 22c6ef943e..41142bf305 100644 --- a/core/debugger/engine_debugger.h +++ b/core/debugger/engine_debugger.h @@ -128,7 +128,7 @@ public:  	virtual void poll_events(bool p_is_idle) {}  	virtual void send_message(const String &p_msg, const Array &p_data) = 0; -	virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) = 0; +	virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) = 0;  	virtual void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false) = 0;  	virtual ~EngineDebugger(); diff --git a/core/debugger/local_debugger.cpp b/core/debugger/local_debugger.cpp index b0b3f11424..7b397e88a3 100644 --- a/core/debugger/local_debugger.cpp +++ b/core/debugger/local_debugger.cpp @@ -139,7 +139,7 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {  		// Cache options  		String variable_prefix = options["variable_prefix"]; -		if (line == "") { +		if (line.is_empty()) {  			print_line("\nDebugger Break, Reason: '" + script_lang->debug_get_error() + "'");  			print_line("*Frame " + itos(current_frame) + " - " + script_lang->debug_get_stack_level_source(current_frame) + ":" + itos(script_lang->debug_get_stack_level_line(current_frame)) + " in function '" + script_lang->debug_get_stack_level_function(current_frame) + "'");  			print_line("Enter \"help\" for assistance."); @@ -166,8 +166,8 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {  		} else if (line.begins_with("set")) {  			if (line.get_slice_count(" ") == 1) { -				for (Map<String, String>::Element *E = options.front(); E; E = E->next()) { -					print_line("\t" + E->key() + "=" + E->value()); +				for (const KeyValue<String, String> &E : options) { +					print_line("\t" + E.key + "=" + E.value);  				}  			} else { @@ -249,8 +249,8 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {  				}  				print_line("Breakpoint(s): " + itos(breakpoints.size())); -				for (Map<int, Set<StringName>>::Element *E = breakpoints.front(); E; E = E->next()) { -					print_line("\t" + String(E->value().front()->get()) + ":" + itos(E->key())); +				for (const KeyValue<int, Set<StringName>> &E : breakpoints) { +					print_line("\t" + String(E.value.front()->get()) + ":" + itos(E.key));  				}  			} else { @@ -358,7 +358,7 @@ void LocalDebugger::send_message(const String &p_message, const Array &p_args) {  	// print_line("MESSAGE: '" + p_message + "' - " + String(Variant(p_args)));  } -void LocalDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) { +void LocalDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) {  	print_line("ERROR: '" + (p_descr.is_empty() ? p_err : p_descr) + "'");  } diff --git a/core/debugger/local_debugger.h b/core/debugger/local_debugger.h index e793b2a859..cb59eb82e9 100644 --- a/core/debugger/local_debugger.h +++ b/core/debugger/local_debugger.h @@ -50,7 +50,7 @@ private:  public:  	void debug(bool p_can_continue, bool p_is_error_breakpoint);  	void send_message(const String &p_message, const Array &p_args); -	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type); +	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type);  	LocalDebugger();  	~LocalDebugger(); diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index f865dfe102..87e65e592a 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -79,8 +79,8 @@ public:  		ERR_FAIL_COND_V(p_buffer.size() == 0, 0);  		int total_bandwidth = 0; -		uint32_t timestamp = OS::get_singleton()->get_ticks_msec(); -		uint32_t final_timestamp = timestamp - 1000; +		uint64_t timestamp = OS::get_singleton()->get_ticks_msec(); +		uint64_t final_timestamp = timestamp - 1000;  		int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size(); @@ -179,8 +179,8 @@ public:  		if (pt - last_profile_time > 100) {  			last_profile_time = pt;  			DebuggerMarshalls::NetworkProfilerFrame frame; -			for (Map<ObjectID, NodeInfo>::Element *E = multiplayer_node_data.front(); E; E = E->next()) { -				frame.infos.push_back(E->get()); +			for (const KeyValue<ObjectID, NodeInfo> &E : multiplayer_node_data) { +				frame.infos.push_back(E.value);  			}  			multiplayer_node_data.clear();  			EngineDebugger::get_singleton()->send_message("network:profile_frame", frame.serialize()); @@ -455,7 +455,7 @@ Error RemoteDebugger::_put_msg(String p_message, Array p_data) {  	return err;  } -void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type) { +void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, bool p_editor_notify, ErrorHandlerType p_type) {  	if (p_type == ERR_HANDLER_SCRIPT) {  		return; //ignore script errors, those go through debugger  	} @@ -475,7 +475,7 @@ void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *  	}  	// send_error will lock internally. -	rd->script_debugger->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si); +	rd->script_debugger->send_error(String::utf8(p_func), String::utf8(p_file), p_line, String::utf8(p_err), String::utf8(p_descr), p_editor_notify, p_type, si);  }  void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error) { @@ -605,7 +605,7 @@ void RemoteDebugger::send_message(const String &p_message, const Array &p_args)  	}  } -void RemoteDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type) { +void RemoteDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) {  	ErrorMessage oe;  	oe.error = p_err;  	oe.error_descr = p_descr; diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h index 28e670747e..73799e3f81 100644 --- a/core/debugger/remote_debugger.h +++ b/core/debugger/remote_debugger.h @@ -89,7 +89,7 @@ private:  	PrintHandlerList phl;  	static void _print_handler(void *p_this, const String &p_string, bool p_error);  	ErrorHandlerList eh; -	static void _err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, ErrorHandlerType p_type); +	static void _err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, bool p_editor_notify, ErrorHandlerType p_type);  	ErrorMessage _create_overflow_error(const String &p_what, const String &p_descr);  	Error _put_msg(String p_message, Array p_data); @@ -111,7 +111,7 @@ public:  	// Overrides  	void poll_events(bool p_is_idle);  	void send_message(const String &p_message, const Array &p_args); -	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type); +	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type);  	void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false);  	RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer); diff --git a/core/debugger/script_debugger.cpp b/core/debugger/script_debugger.cpp index 6d1e4ed101..70ec101a03 100644 --- a/core/debugger/script_debugger.cpp +++ b/core/debugger/script_debugger.cpp @@ -100,10 +100,10 @@ void ScriptDebugger::debug(ScriptLanguage *p_lang, bool p_can_continue, bool p_i  	break_lang = prev;  } -void ScriptDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info) { +void ScriptDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info) {  	// Store stack info, this is ugly, but allows us to separate EngineDebugger and ScriptDebugger. There might be a better way.  	error_stack_info.append_array(p_stack_info); -	EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_type); +	EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type);  	error_stack_info.resize(0);  } diff --git a/core/debugger/script_debugger.h b/core/debugger/script_debugger.h index 9f034a5e5d..c1d0170334 100644 --- a/core/debugger/script_debugger.h +++ b/core/debugger/script_debugger.h @@ -71,7 +71,7 @@ public:  	void debug(ScriptLanguage *p_lang, bool p_can_continue = true, bool p_is_error_breakpoint = false);  	ScriptLanguage *get_break_language() const; -	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info); +	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info);  	Vector<StackInfo> get_error_stack_info() const;  	ScriptDebugger() {}  }; diff --git a/core/doc_data.cpp b/core/doc_data.cpp index 4b284a30aa..7334db5cb0 100644 --- a/core/doc_data.cpp +++ b/core/doc_data.cpp @@ -33,7 +33,7 @@  void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo) {  	if (p_retinfo.type == Variant::INT && p_retinfo.hint == PROPERTY_HINT_INT_IS_POINTER) {  		p_method.return_type = p_retinfo.hint_string; -		if (p_method.return_type == "") { +		if (p_method.return_type.is_empty()) {  			p_method.return_type = "void*";  		} else {  			p_method.return_type += "*"; @@ -64,7 +64,7 @@ void DocData::argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const  	if (p_arginfo.type == Variant::INT && p_arginfo.hint == PROPERTY_HINT_INT_IS_POINTER) {  		p_argument.type = p_arginfo.hint_string; -		if (p_argument.type == "") { +		if (p_argument.type.is_empty()) {  			p_argument.type = "void*";  		} else {  			p_argument.type += "*"; diff --git a/core/doc_data.h b/core/doc_data.h index 19dec71927..db83dda8aa 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -70,18 +70,29 @@ public:  		Vector<int> errors_returned;  		bool operator<(const MethodDoc &p_method) const {  			if (name == p_method.name) { -				// Must be a constructor since there is no overloading. -				// We want this arbitrary order for a class "Foo": -				// - 1. Default constructor: Foo() -				// - 2. Copy constructor: Foo(Foo) -				// - 3+. Other constructors Foo(Bar, ...) based on first argument's name -				if (arguments.size() == 0 || p_method.arguments.size() == 0) { // 1. +				// Must be an operator or a constructor since there is no other overloading +				if (name.left(8) == "operator") { +					if (arguments.size() == p_method.arguments.size()) { +						if (arguments.size() == 0) { +							return false; +						} +						return arguments[0].type < p_method.arguments[0].type; +					}  					return arguments.size() < p_method.arguments.size(); +				} else { +					// Must be a constructor +					// We want this arbitrary order for a class "Foo": +					// - 1. Default constructor: Foo() +					// - 2. Copy constructor: Foo(Foo) +					// - 3+. Other constructors Foo(Bar, ...) based on first argument's name +					if (arguments.size() == 0 || p_method.arguments.size() == 0) { // 1. +						return arguments.size() < p_method.arguments.size(); +					} +					if (arguments[0].type == return_type || p_method.arguments[0].type == p_method.return_type) { // 2. +						return (arguments[0].type == return_type) || (p_method.arguments[0].type != p_method.return_type); +					} +					return arguments[0] < p_method.arguments[0];  				} -				if (arguments[0].type == return_type || p_method.arguments[0].type == p_method.return_type) { // 2. -					return (arguments[0].type == return_type) || (p_method.arguments[0].type != p_method.return_type); -				} -				return arguments[0] < p_method.arguments[0];  			}  			return name < p_method.name;  		} @@ -112,6 +123,7 @@ public:  		String setter, getter;  		String default_value;  		bool overridden = false; +		String overrides;  		bool operator<(const PropertyDoc &p_prop) const {  			return name < p_prop.name;  		} @@ -124,7 +136,11 @@ public:  		String description;  		String default_value;  		bool operator<(const ThemeItemDoc &p_theme_item) const { -			return name < p_theme_item.name; +			// First sort by the data type, then by name. +			if (data_type == p_theme_item.data_type) { +				return name < p_theme_item.name; +			} +			return data_type < p_theme_item.data_type;  		}  	}; @@ -140,7 +156,9 @@ public:  		String brief_description;  		String description;  		Vector<TutorialDoc> tutorials; +		Vector<MethodDoc> constructors;  		Vector<MethodDoc> methods; +		Vector<MethodDoc> operators;  		Vector<MethodDoc> signals;  		Vector<ConstantDoc> constants;  		Map<String, String> enums; diff --git a/core/error/error_macros.cpp b/core/error/error_macros.cpp index 272dda97d8..c236f30945 100644 --- a/core/error/error_macros.cpp +++ b/core/error/error_macros.cpp @@ -37,9 +37,16 @@  static ErrorHandlerList *error_handler_list = nullptr;  void add_error_handler(ErrorHandlerList *p_handler) { +	// If p_handler is already in error_handler_list +	// we'd better remove it first then we can add it. +	// This prevent cyclic redundancy. +	remove_error_handler(p_handler); +  	_global_lock(); +  	p_handler->next = error_handler_list;  	error_handler_list = p_handler; +  	_global_unlock();  } @@ -65,45 +72,49 @@ void remove_error_handler(ErrorHandlerList *p_handler) {  	_global_unlock();  } -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type) { -	_err_print_error(p_function, p_file, p_line, p_error, "", p_type); +// Errors without messages. +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, bool p_editor_notify, ErrorHandlerType p_type) { +	_err_print_error(p_function, p_file, p_line, p_error, "", p_editor_notify, p_type);  } -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type) { -	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), "", p_type); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, bool p_editor_notify, ErrorHandlerType p_type) { +	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), "", p_editor_notify, p_type);  } -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type) { -	OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, (Logger::ErrorType)p_type); +// Main error printing function. +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) { +	OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, p_editor_notify, (Logger::ErrorType)p_type);  	_global_lock();  	ErrorHandlerList *l = error_handler_list;  	while (l) { -		l->errfunc(l->userdata, p_function, p_file, p_line, p_error, p_message, p_type); +		l->errfunc(l->userdata, p_function, p_file, p_line, p_error, p_message, p_editor_notify, p_type);  		l = l->next;  	}  	_global_unlock();  } -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type) { -	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_type); +// Errors with message. (All combinations of p_error and p_message as String or char*.) +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) { +	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_editor_notify, p_type);  } -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type) { -	_err_print_error(p_function, p_file, p_line, p_error, p_message.utf8().get_data(), p_type); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, bool p_editor_notify, ErrorHandlerType p_type) { +	_err_print_error(p_function, p_file, p_line, p_error, p_message.utf8().get_data(), p_editor_notify, p_type);  } -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type) { -	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message.utf8().get_data(), p_type); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify, ErrorHandlerType p_type) { +	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message.utf8().get_data(), p_editor_notify, p_type);  } -void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message, bool fatal) { -	String fstr(fatal ? "FATAL: " : ""); +// Index errors. (All combinations of p_message as String or char*.) +void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message, bool p_editor_notify, bool p_fatal) { +	String fstr(p_fatal ? "FATAL: " : "");  	String err(fstr + "Index " + p_index_str + " = " + itos(p_index) + " is out of bounds (" + p_size_str + " = " + itos(p_size) + ").");  	_err_print_error(p_function, p_file, p_line, err.utf8().get_data(), p_message);  } -void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal) { -	_err_print_index_error(p_function, p_file, p_line, p_index, p_size, p_index_str, p_size_str, p_message.utf8().get_data(), fatal); +void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify, bool p_fatal) { +	_err_print_index_error(p_function, p_file, p_line, p_index, p_size, p_index_str, p_size_str, p_message.utf8().get_data(), p_fatal);  } diff --git a/core/error/error_macros.h b/core/error/error_macros.h index f909a67d55..4eb862dce2 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -46,7 +46,7 @@ enum ErrorHandlerType {  // Pointer to the error handler printing function. Reassign to any function to have errors printed.  // Parameters: userdata, function, file, line, error, explanation, type. -typedef void (*ErrorHandlerFunc)(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type); +typedef void (*ErrorHandlerFunc)(void *, const char *, const char *, int p_line, const char *, const char *, bool p_editor_notify, ErrorHandlerType p_type);  struct ErrorHandlerList {  	ErrorHandlerFunc errfunc = nullptr; @@ -61,14 +61,14 @@ void add_error_handler(ErrorHandlerList *p_handler);  void remove_error_handler(ErrorHandlerList *p_handler);  // Functions used by the error macros. -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool fatal = false); -void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal = false); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool p_editor_notify = false, bool fatal = false); +void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify = false, bool fatal = false);  #ifdef __GNUC__  //#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying @@ -89,13 +89,6 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li  #define GENERATE_TRAP() __builtin_trap()  #endif -// Used to strip debug messages in release mode -#ifdef DEBUG_ENABLED -#define DEBUG_STR(m_msg) m_msg -#else -#define DEBUG_STR(m_msg) "" -#endif -  /**   * Error macros.   * WARNING: These macros work in the opposite way to assert(). @@ -135,11 +128,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.   * If not, prints `m_msg` and the current function returns.   */ -#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg)                                                                                \ -	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                                       \ -		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ -		return;                                                                                                                   \ -	} else                                                                                                                        \ +#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg)                                                                     \ +	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                            \ +		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ +		return;                                                                                                        \ +	} else                                                                                                             \ +		((void)0) + +/** + * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. + * If not, prints `m_msg`, notifies in the editor, and the current function returns. + */ +#define ERR_FAIL_INDEX_EDMSG(m_index, m_size, m_msg)                                                                         \ +	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                                  \ +		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ +		return;                                                                                                              \ +	} else                                                                                                                   \  		((void)0)  /** @@ -160,11 +164,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.   * If not, prints `m_msg` and the current function returns `m_retval`.   */ -#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg)                                                                    \ -	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                                       \ -		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ -		return m_retval;                                                                                                          \ -	} else                                                                                                                        \ +#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg)                                                         \ +	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                            \ +		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ +		return m_retval;                                                                                               \ +	} else                                                                                                             \ +		((void)0) + +/** + * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. + * If not, prints `m_msg`, notifies in the editor, and the current function returns `m_retval`. + */ +#define ERR_FAIL_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg)                                                             \ +	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                                  \ +		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ +		return m_retval;                                                                                                     \ +	} else                                                                                                                   \  		((void)0)  /** @@ -189,11 +204,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0.   * If not, prints `m_msg` and the application crashes.   */ -#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg)                                                                                     \ -	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                                             \ -		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg), true); \ -		GENERATE_TRAP();                                                                                                                \ -	} else                                                                                                                              \ +#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg)                                                                          \ +	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                                  \ +		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ +		GENERATE_TRAP();                                                                                                     \ +	} else                                                                                                                   \  		((void)0)  // Unsigned integer index out of bounds error macros. @@ -216,11 +231,21 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * Ensures an unsigned integer index `m_index` is less than `m_size`.   * If not, prints `m_msg` and the current function returns.   */ -#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg)                                                                       \ -	if (unlikely((m_index) >= (m_size))) {                                                                                        \ -		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ -		return;                                                                                                                   \ -	} else                                                                                                                        \ +#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg)                                                            \ +	if (unlikely((m_index) >= (m_size))) {                                                                             \ +		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ +		return;                                                                                                        \ +	} else                                                                                                             \ +		((void)0) +/** + * Ensures an unsigned integer index `m_index` is less than `m_size`. + * If not, prints `m_msg`, notifies in the editor, and the current function returns. + */ +#define ERR_FAIL_UNSIGNED_INDEX_EDMSG(m_index, m_size, m_msg)                                                                \ +	if (unlikely((m_index) >= (m_size))) {                                                                                   \ +		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ +		return;                                                                                                              \ +	} else                                                                                                                   \  		((void)0)  /** @@ -241,11 +266,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * Ensures an unsigned integer index `m_index` is less than `m_size`.   * If not, prints `m_msg` and the current function returns `m_retval`.   */ -#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg)                                                           \ -	if (unlikely((m_index) >= (m_size))) {                                                                                        \ -		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg)); \ -		return m_retval;                                                                                                          \ -	} else                                                                                                                        \ +#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg)                                                \ +	if (unlikely((m_index) >= (m_size))) {                                                                             \ +		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ +		return m_retval;                                                                                               \ +	} else                                                                                                             \ +		((void)0) + +/** + * Ensures an unsigned integer index `m_index` is less than `m_size`. + * If not, prints `m_msg`, notifies in the editor, and the current function returns `m_retval`. + */ +#define ERR_FAIL_UNSIGNED_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg)                                                    \ +	if (unlikely((m_index) >= (m_size))) {                                                                                   \ +		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ +		return m_retval;                                                                                                     \ +	} else                                                                                                                   \  		((void)0)  /** @@ -270,11 +306,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * Ensures an unsigned integer index `m_index` is less than `m_size`.   * If not, prints `m_msg` and the application crashes.   */ -#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg)                                                                            \ -	if (unlikely((m_index) >= (m_size))) {                                                                                              \ -		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), DEBUG_STR(m_msg), true); \ -		GENERATE_TRAP();                                                                                                                \ -	} else                                                                                                                              \ +#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg)                                                                 \ +	if (unlikely((m_index) >= (m_size))) {                                                                                   \ +		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ +		GENERATE_TRAP();                                                                                                     \ +	} else                                                                                                                   \  		((void)0)  // Null reference error macros. @@ -297,11 +333,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * Ensures a pointer `m_param` is not null.   * If it is null, prints `m_msg` and the current function returns.   */ -#define ERR_FAIL_NULL_MSG(m_param, m_msg)                                                                                 \ -	if (unlikely(m_param == nullptr)) {                                                                                   \ -		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \ -		return;                                                                                                           \ -	} else                                                                                                                \ +#define ERR_FAIL_NULL_MSG(m_param, m_msg)                                                                      \ +	if (unlikely(m_param == nullptr)) {                                                                        \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \ +		return;                                                                                                \ +	} else                                                                                                     \ +		((void)0) + +/** + * Ensures a pointer `m_param` is not null. + * If it is null, prints `m_msg`, notifies in the editor, and the current function returns. + */ +#define ERR_FAIL_NULL_EDMSG(m_param, m_msg)                                                                          \ +	if (unlikely(m_param == nullptr)) {                                                                              \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \ +		return;                                                                                                      \ +	} else                                                                                                           \  		((void)0)  /** @@ -322,11 +369,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * Ensures a pointer `m_param` is not null.   * If it is null, prints `m_msg` and the current function returns `m_retval`.   */ -#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg)                                                                     \ -	if (unlikely(m_param == nullptr)) {                                                                                   \ -		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \ -		return m_retval;                                                                                                  \ -	} else                                                                                                                \ +#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg)                                                          \ +	if (unlikely(m_param == nullptr)) {                                                                        \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \ +		return m_retval;                                                                                       \ +	} else                                                                                                     \ +		((void)0) + +/** + * Ensures a pointer `m_param` is not null. + * If it is null, prints `m_msg`, notifies in the editor, and the current function returns `m_retval`. + */ +#define ERR_FAIL_NULL_V_EDMSG(m_param, m_retval, m_msg)                                                              \ +	if (unlikely(m_param == nullptr)) {                                                                              \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \ +		return m_retval;                                                                                             \ +	} else                                                                                                           \  		((void)0)  /** @@ -352,11 +410,25 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * If checking for null use ERR_FAIL_NULL_MSG instead.   * If checking index bounds use ERR_FAIL_INDEX_MSG instead.   */ -#define ERR_FAIL_COND_MSG(m_cond, m_msg)                                                                                 \ -	if (unlikely(m_cond)) {                                                                                              \ -		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \ -		return;                                                                                                          \ -	} else                                                                                                               \ +#define ERR_FAIL_COND_MSG(m_cond, m_msg)                                                                      \ +	if (unlikely(m_cond)) {                                                                                   \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg); \ +		return;                                                                                               \ +	} else                                                                                                    \ +		((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg`, notifies in the editor, and the current function returns. + * + * If checking for null use ERR_FAIL_NULL_MSG instead. + * If checking index bounds use ERR_FAIL_INDEX_MSG instead. + */ +#define ERR_FAIL_COND_EDMSG(m_cond, m_msg)                                                                          \ +	if (unlikely(m_cond)) {                                                                                         \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg, true); \ +		return;                                                                                                     \ +	} else                                                                                                          \  		((void)0)  /** @@ -382,11 +454,25 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * If checking for null use ERR_FAIL_NULL_V_MSG instead.   * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead.   */ -#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg)                                                                                                \ -	if (unlikely(m_cond)) {                                                                                                                         \ -		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), DEBUG_STR(m_msg)); \ -		return m_retval;                                                                                                                            \ -	} else                                                                                                                                          \ +#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg)                                                                                     \ +	if (unlikely(m_cond)) {                                                                                                              \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg); \ +		return m_retval;                                                                                                                 \ +	} else                                                                                                                               \ +		((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg`, notifies in the editor, and the current function returns `m_retval`. + * + * If checking for null use ERR_FAIL_NULL_V_MSG instead. + * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead. + */ +#define ERR_FAIL_COND_V_EDMSG(m_cond, m_retval, m_msg)                                                                                         \ +	if (unlikely(m_cond)) {                                                                                                                    \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg, true); \ +		return m_retval;                                                                                                                       \ +	} else                                                                                                                                     \  		((void)0)  /** @@ -407,11 +493,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * Ensures `m_cond` is false.   * If `m_cond` is true, prints `m_msg` and the current loop continues.   */ -#define ERR_CONTINUE_MSG(m_cond, m_msg)                                                                                              \ -	if (unlikely(m_cond)) {                                                                                                          \ -		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", DEBUG_STR(m_msg)); \ -		continue;                                                                                                                    \ -	} else                                                                                                                           \ +#define ERR_CONTINUE_MSG(m_cond, m_msg)                                                                                   \ +	if (unlikely(m_cond)) {                                                                                               \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg); \ +		continue;                                                                                                         \ +	} else                                                                                                                \ +		((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg`, notifies in the editor, and the current loop continues. + */ +#define ERR_CONTINUE_EDMSG(m_cond, m_msg)                                                                                       \ +	if (unlikely(m_cond)) {                                                                                                     \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg, true); \ +		continue;                                                                                                               \ +	} else                                                                                                                      \  		((void)0)  /** @@ -432,11 +529,22 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * Ensures `m_cond` is false.   * If `m_cond` is true, prints `m_msg` and the current loop breaks.   */ -#define ERR_BREAK_MSG(m_cond, m_msg)                                                                                               \ -	if (unlikely(m_cond)) {                                                                                                        \ -		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", DEBUG_STR(m_msg)); \ -		break;                                                                                                                     \ -	} else                                                                                                                         \ +#define ERR_BREAK_MSG(m_cond, m_msg)                                                                                    \ +	if (unlikely(m_cond)) {                                                                                             \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg); \ +		break;                                                                                                          \ +	} else                                                                                                              \ +		((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg`, notifies in the editor, and the current loop breaks. + */ +#define ERR_BREAK_EDMSG(m_cond, m_msg)                                                                                        \ +	if (unlikely(m_cond)) {                                                                                                   \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg, true); \ +		break;                                                                                                                \ +	} else                                                                                                                    \  		((void)0)  /** @@ -461,11 +569,11 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * Ensures `m_cond` is false.   * If `m_cond` is true, prints `m_msg` and the application crashes.   */ -#define CRASH_COND_MSG(m_cond, m_msg)                                                                                           \ -	if (unlikely(m_cond)) {                                                                                                     \ -		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", DEBUG_STR(m_msg)); \ -		GENERATE_TRAP();                                                                                                        \ -	} else                                                                                                                      \ +#define CRASH_COND_MSG(m_cond, m_msg)                                                                                \ +	if (unlikely(m_cond)) {                                                                                          \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", m_msg); \ +		GENERATE_TRAP();                                                                                             \ +	} else                                                                                                           \  		((void)0)  // Generic error macros. @@ -490,11 +598,24 @@ 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 (true) {                                                                                          \ -		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", DEBUG_STR(m_msg)); \ -		return;                                                                                          \ -	} else                                                                                               \ +#define ERR_FAIL_MSG(m_msg)                                                                   \ +	if (true) {                                                                               \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg); \ +		return;                                                                               \ +	} else                                                                                    \ +		((void)0) + +/** + * Try using `ERR_FAIL_COND_MSG`. + * Only use this macro if more complex error detection or recovery is required. + * + * Prints `m_msg`, notifies in the editor, and the current function returns. + */ +#define ERR_FAIL_EDMSG(m_msg)                                                                       \ +	if (true) {                                                                                     \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg, true); \ +		return;                                                                                     \ +	} else                                                                                          \  		((void)0)  /** @@ -517,11 +638,24 @@ 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 (true) {                                                                                                                     \ -		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), DEBUG_STR(m_msg)); \ -		return m_retval;                                                                                                            \ -	} else                                                                                                                          \ +#define ERR_FAIL_V_MSG(m_retval, m_msg)                                                                                  \ +	if (true) {                                                                                                          \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg); \ +		return m_retval;                                                                                                 \ +	} else                                                                                                               \ +		((void)0) + +/** + * Try using `ERR_FAIL_COND_V_MSG`. + * Only use this macro if more complex error detection or recovery is required. + * + * Prints `m_msg`, notifies in the editor, and the current function returns `m_retval`. + */ +#define ERR_FAIL_V_EDMSG(m_retval, m_msg)                                                                                      \ +	if (true) {                                                                                                                \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg, true); \ +		return m_retval;                                                                                                       \ +	} else                                                                                                                     \  		((void)0)  /** @@ -535,6 +669,16 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li  	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg)  /** + * Try using `ERR_FAIL_COND_MSG`, `ERR_FAIL_COND_V_MSG`, `ERR_CONTINUE_MSG` or ERR_BREAK_MSG. + * Only use this macro at the start of a function that has not been implemented yet, or + * if more complex error detection or recovery is required. + * + * Prints `m_msg` and notifies the editor. + */ +#define ERR_PRINT_ED(m_msg) \ +	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ) + +/**   * Prints `m_msg` once during the application lifetime.   */  #define ERR_PRINT_ONCE(m_msg)                                          \ @@ -547,6 +691,19 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li  	} else                                                             \  		((void)0) +/** + * Prints `m_msg` and notifies the editor once during the application lifetime. + */ +#define ERR_PRINT_ONCE_ED(m_msg)                                             \ +	if (true) {                                                              \ +		static bool first_print = true;                                      \ +		if (first_print) {                                                   \ +			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true); \ +			first_print = false;                                             \ +		}                                                                    \ +	} else                                                                   \ +		((void)0) +  // Print warning message macros.  /** @@ -555,21 +712,44 @@ 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(m_msg) \ -	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ERR_HANDLER_WARNING) +	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, false, ERR_HANDLER_WARNING) + +/** + * Prints `m_msg` and notifies the editor. + * + * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. + */ +#define WARN_PRINT_ED(m_msg) \ +	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true, ERR_HANDLER_WARNING)  /**   * Prints `m_msg` once during the application lifetime.   *   * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.   */ -#define WARN_PRINT_ONCE(m_msg)                                                              \ -	if (true) {                                                                             \ -		static bool first_print = true;                                                     \ -		if (first_print) {                                                                  \ -			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, ERR_HANDLER_WARNING); \ -			first_print = false;                                                            \ -		}                                                                                   \ -	} else                                                                                  \ +#define WARN_PRINT_ONCE(m_msg)                                                                     \ +	if (true) {                                                                                    \ +		static bool first_print = true;                                                            \ +		if (first_print) {                                                                         \ +			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, false, ERR_HANDLER_WARNING); \ +			first_print = false;                                                                   \ +		}                                                                                          \ +	} else                                                                                         \ +		((void)0) + +/** + * Prints `m_msg` and notifies the editor once during the application lifetime. + * + * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. + */ +#define WARN_PRINT_ONCE_ED(m_msg)                                                                 \ +	if (true) {                                                                                   \ +		static bool first_print = true;                                                           \ +		if (first_print) {                                                                        \ +			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true, ERR_HANDLER_WARNING); \ +			first_print = false;                                                                  \ +		}                                                                                         \ +	} else                                                                                        \  		((void)0)  // Print deprecated warning message macros. @@ -577,27 +757,27 @@ 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 (true) {                                                                                                                                            \ -		static SafeFlag warning_shown;                                                                                                                     \ -		if (!warning_shown.is_set()) {                                                                                                                     \ -			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", ERR_HANDLER_WARNING); \ -			warning_shown.set();                                                                                                                           \ -		}                                                                                                                                                  \ -	} else                                                                                                                                                 \ +#define WARN_DEPRECATED                                                                                                                                           \ +	if (true) {                                                                                                                                                   \ +		static SafeFlag warning_shown;                                                                                                                            \ +		if (!warning_shown.is_set()) {                                                                                                                            \ +			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", false, ERR_HANDLER_WARNING); \ +			warning_shown.set();                                                                                                                                  \ +		}                                                                                                                                                         \ +	} else                                                                                                                                                        \  		((void)0)  /**   * Warns that the current function is deprecated and prints `m_msg`.   */ -#define WARN_DEPRECATED_MSG(m_msg)                                                                                                                                           \ -	if (true) {                                                                                                                                                              \ -		static SafeFlag warning_shown;                                                                                                                                       \ -		if (!warning_shown.is_set()) {                                                                                                                                       \ -			_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); \ -			warning_shown.set();                                                                                                                                             \ -		}                                                                                                                                                                    \ -	} else                                                                                                                                                                   \ +#define WARN_DEPRECATED_MSG(m_msg)                                                                                                                                       \ +	if (true) {                                                                                                                                                          \ +		static SafeFlag warning_shown;                                                                                                                                   \ +		if (!warning_shown.is_set()) {                                                                                                                                   \ +			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, false, ERR_HANDLER_WARNING); \ +			warning_shown.set();                                                                                                                                         \ +		}                                                                                                                                                                \ +	} else                                                                                                                                                               \  		((void)0)  /** @@ -618,11 +798,11 @@ 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 (true) {                                                                                                 \ -		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", DEBUG_STR(m_msg)); \ -		GENERATE_TRAP();                                                                                        \ -	} else                                                                                                      \ +#define CRASH_NOW_MSG(m_msg)                                                                         \ +	if (true) {                                                                                      \ +		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", m_msg); \ +		GENERATE_TRAP();                                                                             \ +	} else                                                                                           \  		((void)0)  #endif // ERROR_MACROS_H diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index a8547a0090..fc74293d0d 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -40,7 +40,7 @@  static String get_type_name(const PropertyInfo &p_info) {  	if (p_info.type == Variant::INT && (p_info.hint == PROPERTY_HINT_INT_IS_POINTER)) { -		if (p_info.hint_string == "") { +		if (p_info.hint_string.is_empty()) {  			return "void*";  		} else {  			return p_info.hint_string + "*"; @@ -340,7 +340,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {  			int value = CoreConstants::get_global_constant_value(i);  			String enum_name = CoreConstants::get_global_constant_enum(i);  			String name = CoreConstants::get_global_constant_name(i); -			if (enum_name != String()) { +			if (!enum_name.is_empty()) {  				enum_list[enum_name].push_back(Pair<String, int>(name, value));  			} else {  				Dictionary d; @@ -353,11 +353,11 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {  		api_dump["global_constants"] = constants;  		Array enums; -		for (Map<String, List<Pair<String, int>>>::Element *E = enum_list.front(); E; E = E->next()) { +		for (const KeyValue<String, List<Pair<String, int>>> &E : enum_list) {  			Dictionary d1; -			d1["name"] = E->key(); +			d1["name"] = E.key;  			Array values; -			for (const Pair<String, int> &F : E->get()) { +			for (const Pair<String, int> &F : E.value) {  				Dictionary d2;  				d2["name"] = F.first;  				d2["value"] = F.second; @@ -841,6 +841,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {  	{  		Array native_structures; +		// AudioStream structures  		{  			Dictionary d;  			d["name"] = "AudioFrame"; @@ -849,6 +850,22 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {  			native_structures.push_back(d);  		} +		// TextServer structures +		{ +			Dictionary d; +			d["name"] = "Glyph"; +			d["format"] = "int start,int end,uint8_t count,uint8_t repeat,uint16_t flags,float x_off,float y_off,float advance,RID font_rid,int font_size,int32_t index"; + +			native_structures.push_back(d); +		} +		{ +			Dictionary d; +			d["name"] = "CaretInfo"; +			d["format"] = "Rect2 leading_caret,Rect2 trailing_caret,TextServer::Direction leading_direction,TextServer::Direction trailing_direction"; + +			native_structures.push_back(d); +		} +  		api_dump["native_structures"] = native_structures;  	} diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp index de107b4156..0c9b344a37 100644 --- a/core/extension/gdnative_interface.cpp +++ b/core/extension/gdnative_interface.cpp @@ -51,13 +51,13 @@ static void gdnative_free(void *p_mem) {  // Helper print functions.  static void gdnative_print_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) { -	_err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_ERROR); +	_err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_ERROR);  }  static void gdnative_print_warning(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) { -	_err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_WARNING); +	_err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_WARNING);  }  static void gdnative_print_script_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) { -	_err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_SCRIPT); +	_err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_SCRIPT);  }  // Variant functions @@ -661,6 +661,140 @@ static const char32_t *gdnative_string_operator_index_const(const GDNativeString  	return &self->ptr()[p_index];  } +/* Packed array functions */ + +static uint8_t *gdnative_packed_byte_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { +	PackedByteArray *self = (PackedByteArray *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return &self->ptrw()[p_index]; +} + +static const uint8_t *gdnative_packed_byte_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { +	const PackedByteArray *self = (const PackedByteArray *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return &self->ptr()[p_index]; +} + +static GDNativeTypePtr gdnative_packed_color_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { +	PackedColorArray *self = (PackedColorArray *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return (GDNativeTypePtr)&self->ptrw()[p_index]; +} + +static GDNativeTypePtr gdnative_packed_color_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { +	const PackedColorArray *self = (const PackedColorArray *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return (GDNativeTypePtr)&self->ptr()[p_index]; +} + +static float *gdnative_packed_float32_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { +	PackedFloat32Array *self = (PackedFloat32Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return &self->ptrw()[p_index]; +} + +static const float *gdnative_packed_float32_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { +	const PackedFloat32Array *self = (const PackedFloat32Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return &self->ptr()[p_index]; +} + +static double *gdnative_packed_float64_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { +	PackedFloat64Array *self = (PackedFloat64Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return &self->ptrw()[p_index]; +} + +static const double *gdnative_packed_float64_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { +	const PackedFloat64Array *self = (const PackedFloat64Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return &self->ptr()[p_index]; +} + +static int32_t *gdnative_packed_int32_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { +	PackedInt32Array *self = (PackedInt32Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return &self->ptrw()[p_index]; +} + +static const int32_t *gdnative_packed_int32_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { +	const PackedInt32Array *self = (const PackedInt32Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return &self->ptr()[p_index]; +} + +static int64_t *gdnative_packed_int64_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { +	PackedInt64Array *self = (PackedInt64Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return &self->ptrw()[p_index]; +} + +static const int64_t *gdnative_packed_int64_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { +	const PackedInt64Array *self = (const PackedInt64Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return &self->ptr()[p_index]; +} + +static GDNativeStringPtr gdnative_packed_string_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { +	PackedStringArray *self = (PackedStringArray *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return (GDNativeStringPtr)&self->ptrw()[p_index]; +} + +static GDNativeStringPtr gdnative_packed_string_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { +	const PackedStringArray *self = (const PackedStringArray *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return (GDNativeStringPtr)&self->ptr()[p_index]; +} + +static GDNativeTypePtr gdnative_packed_vector2_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { +	PackedVector2Array *self = (PackedVector2Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return (GDNativeTypePtr)&self->ptrw()[p_index]; +} + +static GDNativeTypePtr gdnative_packed_vector2_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { +	const PackedVector2Array *self = (const PackedVector2Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return (GDNativeTypePtr)&self->ptr()[p_index]; +} + +static GDNativeTypePtr gdnative_packed_vector3_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { +	PackedVector3Array *self = (PackedVector3Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return (GDNativeTypePtr)&self->ptrw()[p_index]; +} + +static GDNativeTypePtr gdnative_packed_vector3_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { +	const PackedVector3Array *self = (const PackedVector3Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return (GDNativeTypePtr)&self->ptr()[p_index]; +} + +static GDNativeVariantPtr gdnative_array_operator_index(GDNativeTypePtr p_self, GDNativeInt p_index) { +	Array *self = (Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return (GDNativeVariantPtr)&self->operator[](p_index); +} + +static GDNativeVariantPtr gdnative_array_operator_index_const(const GDNativeTypePtr p_self, GDNativeInt p_index) { +	const Array *self = (const Array *)p_self; +	ERR_FAIL_INDEX_V(p_index, self->size(), nullptr); +	return (GDNativeVariantPtr)&self->operator[](p_index); +} + +/* Dictionary functions */ + +static GDNativeVariantPtr gdnative_dictionary_operator_index(GDNativeTypePtr p_self, const GDNativeVariantPtr p_key) { +	Dictionary *self = (Dictionary *)p_self; +	return (GDNativeVariantPtr)&self->operator[](*(const Variant *)p_key); +} + +static GDNativeVariantPtr gdnative_dictionary_operator_index_const(const GDNativeTypePtr p_self, const GDNativeVariantPtr p_key) { +	const Dictionary *self = (const Dictionary *)p_self; +	return (GDNativeVariantPtr)&self->operator[](*(const Variant *)p_key); +} +  /* OBJECT API */  static void gdnative_object_method_bind_call(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error) { @@ -693,16 +827,21 @@ static GDNativeObjectPtr gdnative_global_get_singleton(const char *p_name) {  	return (GDNativeObjectPtr)Engine::get_singleton()->get_singleton_object(String(p_name));  } -static void *gdnative_object_get_instance_binding(GDNativeObjectPtr p_instance, void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks) { -	Object *o = (Object *)p_instance; +static void *gdnative_object_get_instance_binding(GDNativeObjectPtr p_object, void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks) { +	Object *o = (Object *)p_object;  	return o->get_instance_binding(p_token, p_callbacks);  } -static void gdnative_object_set_instance_binding(GDNativeObjectPtr p_instance, void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks) { -	Object *o = (Object *)p_instance; +static void gdnative_object_set_instance_binding(GDNativeObjectPtr p_object, void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks) { +	Object *o = (Object *)p_object;  	o->set_instance_binding(p_token, p_binding, p_callbacks);  } +static void gdnative_object_set_instance(GDNativeObjectPtr p_object, const char *p_classname, GDExtensionClassInstancePtr p_instance) { +	Object *o = (Object *)p_object; +	ClassDB::set_object_extension_instance(o, p_classname, p_instance); +} +  static GDNativeObjectPtr gdnative_object_get_instance_from_id(GDObjectInstanceID p_instance_id) {  	return (GDNativeObjectPtr)ObjectDB::get_instance(ObjectID(p_instance_id));  } @@ -732,12 +871,8 @@ static GDNativeMethodBindPtr gdnative_classdb_get_method_bind(const char *p_clas  	return (GDNativeMethodBindPtr)mb;  } -static GDNativeClassConstructor gdnative_classdb_get_constructor(const char *p_classname) { -	ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(StringName(p_classname)); -	if (class_info) { -		return (GDNativeClassConstructor)class_info->creation_func; -	} -	return nullptr; +static GDNativeObjectPtr gdnative_classdb_construct_object(const char *p_classname) { +	return (GDNativeObjectPtr)ClassDB::instantiate(p_classname);  }  static void *gdnative_classdb_get_class_tag(const char *p_classname) { @@ -843,6 +978,40 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) {  	gdni.string_operator_index = gdnative_string_operator_index;  	gdni.string_operator_index_const = gdnative_string_operator_index_const; +	/* Packed array functions */ + +	gdni.packed_byte_array_operator_index = gdnative_packed_byte_array_operator_index; +	gdni.packed_byte_array_operator_index_const = gdnative_packed_byte_array_operator_index_const; + +	gdni.packed_color_array_operator_index = gdnative_packed_color_array_operator_index; +	gdni.packed_color_array_operator_index_const = gdnative_packed_color_array_operator_index_const; + +	gdni.packed_float32_array_operator_index = gdnative_packed_float32_array_operator_index; +	gdni.packed_float32_array_operator_index_const = gdnative_packed_float32_array_operator_index_const; +	gdni.packed_float64_array_operator_index = gdnative_packed_float64_array_operator_index; +	gdni.packed_float64_array_operator_index_const = gdnative_packed_float64_array_operator_index_const; + +	gdni.packed_int32_array_operator_index = gdnative_packed_int32_array_operator_index; +	gdni.packed_int32_array_operator_index_const = gdnative_packed_int32_array_operator_index_const; +	gdni.packed_int64_array_operator_index = gdnative_packed_int64_array_operator_index; +	gdni.packed_int64_array_operator_index_const = gdnative_packed_int64_array_operator_index_const; + +	gdni.packed_string_array_operator_index = gdnative_packed_string_array_operator_index; +	gdni.packed_string_array_operator_index_const = gdnative_packed_string_array_operator_index_const; + +	gdni.packed_vector2_array_operator_index = gdnative_packed_vector2_array_operator_index; +	gdni.packed_vector2_array_operator_index_const = gdnative_packed_vector2_array_operator_index_const; +	gdni.packed_vector3_array_operator_index = gdnative_packed_vector3_array_operator_index; +	gdni.packed_vector3_array_operator_index_const = gdnative_packed_vector3_array_operator_index_const; + +	gdni.array_operator_index = gdnative_array_operator_index; +	gdni.array_operator_index_const = gdnative_array_operator_index_const; + +	/* Dictionary functions */ + +	gdni.dictionary_operator_index = gdnative_dictionary_operator_index; +	gdni.dictionary_operator_index_const = gdnative_dictionary_operator_index_const; +  	/* OBJECT */  	gdni.object_method_bind_call = gdnative_object_method_bind_call; @@ -851,6 +1020,7 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) {  	gdni.global_get_singleton = gdnative_global_get_singleton;  	gdni.object_get_instance_binding = gdnative_object_get_instance_binding;  	gdni.object_set_instance_binding = gdnative_object_set_instance_binding; +	gdni.object_set_instance = gdnative_object_set_instance;  	gdni.object_cast_to = gdnative_object_cast_to;  	gdni.object_get_instance_from_id = gdnative_object_get_instance_from_id; @@ -858,7 +1028,7 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) {  	/* CLASSDB */ -	gdni.classdb_get_constructor = gdnative_classdb_get_constructor; +	gdni.classdb_construct_object = gdnative_classdb_construct_object;  	gdni.classdb_get_method_bind = gdnative_classdb_get_method_bind;  	gdni.classdb_get_class_tag = gdnative_classdb_get_class_tag; @@ -869,6 +1039,8 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) {  	gdni.classdb_register_extension_class_method = nullptr;  	gdni.classdb_register_extension_class_integer_constant = nullptr;  	gdni.classdb_register_extension_class_property = nullptr; +	gdni.classdb_register_extension_class_property_group = nullptr; +	gdni.classdb_register_extension_class_property_subgroup = nullptr;  	gdni.classdb_register_extension_class_signal = nullptr;  	gdni.classdb_unregister_extension_class = nullptr;  } diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h index 3a5b04429c..2191d99dea 100644 --- a/core/extension/gdnative_interface.h +++ b/core/extension/gdnative_interface.h @@ -137,6 +137,7 @@ typedef void *GDNativeStringNamePtr;  typedef void *GDNativeStringPtr;  typedef void *GDNativeObjectPtr;  typedef void *GDNativeTypePtr; +typedef void *GDNativeExtensionPtr;  typedef void *GDNativeMethodBindPtr;  typedef int64_t GDNativeInt;  typedef uint8_t GDNativeBool; @@ -181,11 +182,11 @@ typedef void *(*GDNativeInstanceBindingCreateCallback)(void *p_token, void *p_in  typedef void (*GDNativeInstanceBindingFreeCallback)(void *p_token, void *p_instance, void *p_binding);  typedef GDNativeBool (*GDNativeInstanceBindingReferenceCallback)(void *p_token, void *p_binding, GDNativeBool p_reference); -struct GDNativeInstanceBindingCallbacks { +typedef struct {  	GDNativeInstanceBindingCreateCallback create_callback;  	GDNativeInstanceBindingFreeCallback free_callback;  	GDNativeInstanceBindingReferenceCallback reference_callback; -}; +} GDNativeInstanceBindingCallbacks;  /* EXTENSION CLASSES */ @@ -210,7 +211,7 @@ typedef const char *(*GDNativeExtensionClassToString)(GDExtensionClassInstancePt  typedef void (*GDNativeExtensionClassReference)(GDExtensionClassInstancePtr p_instance);  typedef void (*GDNativeExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);  typedef void (*GDNativeExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); -typedef GDExtensionClassInstancePtr (*GDNativeExtensionClassCreateInstance)(void *p_userdata); +typedef GDNativeObjectPtr (*GDNativeExtensionClassCreateInstance)(void *p_userdata);  typedef void (*GDNativeExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance);  typedef void (*GDNativeExtensionClassObjectInstance)(GDExtensionClassInstancePtr p_instance, GDNativeObjectPtr p_object_instance);  typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, const char *p_name); @@ -226,7 +227,6 @@ typedef struct {  	GDNativeExtensionClassUnreference unreference_func;  	GDNativeExtensionClassCreateInstance create_instance_func; /* this one is mandatory */  	GDNativeExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ -	GDNativeExtensionClassObjectInstance object_instance_func; /* this one is mandatory */  	GDNativeExtensionClassGetVirtual get_virtual_func;  	void *class_userdata;  } GDNativeExtensionClassCreationInfo; @@ -387,22 +387,58 @@ typedef struct {  	char32_t *(*string_operator_index)(GDNativeStringPtr p_self, GDNativeInt p_index);  	const char32_t *(*string_operator_index_const)(const GDNativeStringPtr p_self, GDNativeInt p_index); +	/* Packed array functions */ + +	uint8_t *(*packed_byte_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedByteArray +	const uint8_t *(*packed_byte_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedByteArray + +	GDNativeTypePtr (*packed_color_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedColorArray, returns Color ptr +	GDNativeTypePtr (*packed_color_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedColorArray, returns Color ptr + +	float *(*packed_float32_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat32Array +	const float *(*packed_float32_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat32Array +	double *(*packed_float64_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat64Array +	const double *(*packed_float64_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat64Array + +	int32_t *(*packed_int32_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array +	const int32_t *(*packed_int32_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array +	int64_t *(*packed_int64_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array +	const int64_t *(*packed_int64_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array + +	GDNativeStringPtr (*packed_string_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedStringArray +	GDNativeStringPtr (*packed_string_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedStringArray + +	GDNativeTypePtr (*packed_vector2_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr +	GDNativeTypePtr (*packed_vector2_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr +	GDNativeTypePtr (*packed_vector3_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr +	GDNativeTypePtr (*packed_vector3_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr + +	GDNativeVariantPtr (*array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be an Array ptr +	GDNativeVariantPtr (*array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be an Array ptr + +	/* Dictionary functions */ + +	GDNativeVariantPtr (*dictionary_operator_index)(GDNativeTypePtr p_self, const GDNativeVariantPtr p_key); // p_self should be an Dictionary ptr +	GDNativeVariantPtr (*dictionary_operator_index_const)(const GDNativeTypePtr p_self, const GDNativeVariantPtr p_key); // p_self should be an Dictionary ptr +  	/* OBJECT */  	void (*object_method_bind_call)(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_ret, GDNativeCallError *r_error);  	void (*object_method_bind_ptrcall)(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret);  	void (*object_destroy)(GDNativeObjectPtr p_o);  	GDNativeObjectPtr (*global_get_singleton)(const char *p_name); +  	void *(*object_get_instance_binding)(GDNativeObjectPtr p_o, void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks);  	void (*object_set_instance_binding)(GDNativeObjectPtr p_o, void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks); +	void (*object_set_instance)(GDNativeObjectPtr p_o, const char *p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */ +  	GDNativeObjectPtr (*object_cast_to)(const GDNativeObjectPtr p_object, void *p_class_tag);  	GDNativeObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id);  	GDObjectInstanceID (*object_get_instance_id)(const GDNativeObjectPtr p_object);  	/* CLASSDB */ - -	GDNativeClassConstructor (*classdb_get_constructor)(const char *p_classname); +	GDNativeObjectPtr (*classdb_construct_object)(const char *p_classname); /* The passed class must be a built-in godot class, or an already-registered extension class. In both case, object_set_instance should be called to fully initialize the object. */  	GDNativeMethodBindPtr (*classdb_get_method_bind)(const char *p_classname, const char *p_methodname, GDNativeInt p_hash);  	void *(*classdb_get_class_tag)(const char *p_classname); @@ -412,6 +448,8 @@ typedef struct {  	void (*classdb_register_extension_class_method)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info);  	void (*classdb_register_extension_class_integer_constant)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_enum_name, const char *p_constant_name, GDNativeInt p_constant_value);  	void (*classdb_register_extension_class_property)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter); +	void (*classdb_register_extension_class_property_group)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_group_name, const char *p_prefix); +	void (*classdb_register_extension_class_property_subgroup)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_subgroup_name, const char *p_prefix);  	void (*classdb_register_extension_class_signal)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count);  	void (*classdb_unregister_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */  } GDNativeInterface; @@ -423,6 +461,8 @@ typedef enum {  	GDNATIVE_INITIALIZATION_SERVERS,  	GDNATIVE_INITIALIZATION_SCENE,  	GDNATIVE_INITIALIZATION_EDITOR, +	GDNATIVE_INITIALIZATION_DRIVER, +	GDNATIVE_MAX_INITIALIZATION_LEVEL,  } GDNativeInitializationLevel;  typedef struct { diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp index a3cd7ca14c..cfd3ac8099 100644 --- a/core/extension/native_extension.cpp +++ b/core/extension/native_extension.cpp @@ -35,7 +35,9 @@  #include "core/object/method_bind.h"  #include "core/os/os.h" -const char *NativeExtension::EXTENSION_LIST_CONFIG_FILE = "res://.godot/extension_list.cfg"; +String NativeExtension::get_extension_list_config_file() { +	return ProjectSettings::get_singleton()->get_project_data_path().plus_file("extension_list.cfg"); +}  class NativeExtensionMethodBind : public MethodBind {  	GDNativeExtensionClassMethodCall call_func; @@ -154,7 +156,6 @@ void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibr  	extension->native_extension.unreference = p_extension_funcs->unreference_func;  	extension->native_extension.class_userdata = p_extension_funcs->class_userdata;  	extension->native_extension.create_instance = p_extension_funcs->create_instance_func; -	extension->native_extension.set_object_instance = p_extension_funcs->object_instance_func;  	extension->native_extension.free_instance = p_extension_funcs->free_instance_func;  	extension->native_extension.get_virtual = p_extension_funcs->get_virtual_func; @@ -184,6 +185,7 @@ void NativeExtension::_register_extension_class_integer_constant(const GDNativeE  	ClassDB::bind_integer_constant(class_name, p_enum_name, p_constant_name, p_constant_value);  } +  void NativeExtension::_register_extension_class_property(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter) {  	NativeExtension *self = (NativeExtension *)p_library; @@ -202,6 +204,24 @@ void NativeExtension::_register_extension_class_property(const GDNativeExtension  	ClassDB::add_property(class_name, pinfo, p_setter, p_getter);  } +void NativeExtension::_register_extension_class_property_group(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_group_name, const char *p_prefix) { +	NativeExtension *self = (NativeExtension *)p_library; + +	StringName class_name = p_class_name; +	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property group '" + String(p_group_name) + "' for unexisting class '" + class_name + "'."); + +	ClassDB::add_property_group(class_name, p_group_name, p_prefix); +} + +void NativeExtension::_register_extension_class_property_subgroup(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_subgroup_name, const char *p_prefix) { +	NativeExtension *self = (NativeExtension *)p_library; + +	StringName class_name = p_class_name; +	ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property subgroup '" + String(p_subgroup_name) + "' for unexisting class '" + class_name + "'."); + +	ClassDB::add_property_subgroup(class_name, p_subgroup_name, p_prefix); +} +  void NativeExtension::_register_extension_class_signal(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count) {  	NativeExtension *self = (NativeExtension *)p_library; @@ -325,6 +345,8 @@ void NativeExtension::initialize_native_extensions() {  	gdnative_interface.classdb_register_extension_class_method = _register_extension_class_method;  	gdnative_interface.classdb_register_extension_class_integer_constant = _register_extension_class_integer_constant;  	gdnative_interface.classdb_register_extension_class_property = _register_extension_class_property; +	gdnative_interface.classdb_register_extension_class_property_group = _register_extension_class_property_group; +	gdnative_interface.classdb_register_extension_class_property_subgroup = _register_extension_class_property_subgroup;  	gdnative_interface.classdb_register_extension_class_signal = _register_extension_class_signal;  	gdnative_interface.classdb_unregister_extension_class = _unregister_extension_class;  } @@ -375,7 +397,7 @@ RES NativeExtensionResourceLoader::load(const String &p_path, const String &p_or  		}  	} -	if (library_path == String()) { +	if (library_path.is_empty()) {  		if (r_error) {  			*r_error = ERR_FILE_NOT_FOUND;  		} diff --git a/core/extension/native_extension.h b/core/extension/native_extension.h index b661381d64..f7f235d8fc 100644 --- a/core/extension/native_extension.h +++ b/core/extension/native_extension.h @@ -50,6 +50,8 @@ class NativeExtension : public Resource {  	static void _register_extension_class_method(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info);  	static void _register_extension_class_integer_constant(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_enum_name, const char *p_constant_name, GDNativeInt p_constant_value);  	static void _register_extension_class_property(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter); +	static void _register_extension_class_property_group(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_group_name, const char *p_prefix); +	static void _register_extension_class_property_subgroup(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_subgroup_name, const char *p_prefix);  	static void _register_extension_class_signal(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count);  	static void _unregister_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name); @@ -60,7 +62,7 @@ protected:  	static void _bind_methods();  public: -	static const char *EXTENSION_LIST_CONFIG_FILE; +	static String get_extension_list_config_file();  	Error open_library(const String &p_path, const String &p_entry_symbol);  	void close_library(); @@ -70,6 +72,7 @@ public:  		INITIALIZATION_LEVEL_SERVERS,  		INITIALIZATION_LEVEL_SCENE,  		INITIALIZATION_LEVEL_EDITOR, +		INITIALIZATION_LEVEL_DRIVER,  	};  	bool is_library_open() const; diff --git a/core/extension/native_extension_manager.cpp b/core/extension/native_extension_manager.cpp index 8b7a9df4f1..043843ea31 100644 --- a/core/extension/native_extension_manager.cpp +++ b/core/extension/native_extension_manager.cpp @@ -84,8 +84,8 @@ bool NativeExtensionManager::is_extension_loaded(const String &p_path) const {  Vector<String> NativeExtensionManager::get_loaded_extensions() const {  	Vector<String> ret; -	for (const Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { -		ret.push_back(E->key()); +	for (const KeyValue<String, Ref<NativeExtension>> &E : native_extension_map) { +		ret.push_back(E.key);  	}  	return ret;  } @@ -97,25 +97,25 @@ Ref<NativeExtension> NativeExtensionManager::get_extension(const String &p_path)  void NativeExtensionManager::initialize_extensions(NativeExtension::InitializationLevel p_level) {  	ERR_FAIL_COND(int32_t(p_level) - 1 != level); -	for (Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { -		E->get()->initialize_library(p_level); +	for (KeyValue<String, Ref<NativeExtension>> &E : native_extension_map) { +		E.value->initialize_library(p_level);  	}  	level = p_level;  }  void NativeExtensionManager::deinitialize_extensions(NativeExtension::InitializationLevel p_level) {  	ERR_FAIL_COND(int32_t(p_level) != level); -	for (Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { -		E->get()->deinitialize_library(p_level); +	for (KeyValue<String, Ref<NativeExtension>> &E : native_extension_map) { +		E.value->deinitialize_library(p_level);  	}  	level = int32_t(p_level) - 1;  }  void NativeExtensionManager::load_extensions() { -	FileAccessRef f = FileAccess::open(NativeExtension::EXTENSION_LIST_CONFIG_FILE, FileAccess::READ); +	FileAccessRef f = FileAccess::open(NativeExtension::get_extension_list_config_file(), FileAccess::READ);  	while (f && !f->eof_reached()) {  		String s = f->get_line().strip_edges(); -		if (s != String()) { +		if (!s.is_empty()) {  			LoadStatus err = load_extension(s);  			ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s);  		} diff --git a/core/input/SCsub b/core/input/SCsub index 740398b266..b12bf561de 100644 --- a/core/input/SCsub +++ b/core/input/SCsub @@ -7,19 +7,15 @@ import input_builders  # Order matters here. Higher index controller database files write on top of lower index database files.  controller_databases = [ -    "#core/input/gamecontrollerdb.txt", -    "#core/input/godotcontrollerdb.txt", +    "gamecontrollerdb.txt", +    "godotcontrollerdb.txt",  ] -env.Depends("#core/input/default_controller_mappings.gen.cpp", controller_databases) -env.CommandNoCache( -    "#core/input/default_controller_mappings.gen.cpp", +gensource = env.CommandNoCache( +    "default_controller_mappings.gen.cpp",      controller_databases,      env.Run(input_builders.make_default_controller_mappings, "Generating default controller mappings."),  )  env.add_source_files(env.core_sources, "*.cpp") - -# Don't warn about duplicate entry here, we need it registered manually for first build, -# even if later builds will pick it up twice due to above *.cpp globbing. -env.add_source_files(env.core_sources, "#core/input/default_controller_mappings.gen.cpp", warn_duplicates=False) +env.add_source_files(env.core_sources, gensource) diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt index f136d83496..75b89918d6 100644 --- a/core/input/gamecontrollerdb.txt +++ b/core/input/gamecontrollerdb.txt @@ -1,25 +1,39 @@ -# Game Controller DB for SDL in 2.0.9 format +# Game Controller DB for SDL in 2.0.16 format  # Source: https://github.com/gabomdq/SDL_GameControllerDB  # Windows  03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows, -03000000c82d00002038000000000000,8bitdo,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, +03000000d0160000040d000000000000,4Play,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, +03000000d0160000050d000000000000,4Play,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, +03000000d0160000060d000000000000,4Play,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, +03000000d0160000070d000000000000,4Play,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, +03000000d0160000600a000000000000,4Play,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,  03000000c82d00000951000000000000,8BitDo Dogbone Modkit,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows, -03000000c82d000011ab000000000000,8BitDo F30,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, +03000000008000000210000000000000,8BitDo F30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +030000003512000011ab000000000000,8BitDo F30 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000c82d00001028000000000000,8BitDo F30 Arcade Joystick,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d000011ab000000000000,8BitDo F30 Arcade Joystick,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, +03000000801000000900000000000000,8BitDo F30 Arcade Stick,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,  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, +03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,guide:b2,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: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,  03000000c82d00000151000000000000,8BitDo M30 ModKit,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,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, +03000000c82d0000e002000000000000,8BitDo N30,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b6,platform:Windows,  03000000c82d00000451000000000000,8BitDo N30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,start:b11,platform:Windows,  03000000c82d00000190000000000000,8BitDo N30 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,  03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,  03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000290000000000000,8Bitdo N64,+rightx:b9,+righty:b3,-rightx:b4,-righty:b8,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,platform:Windows, +03000000c82d00003038000000000000,8Bitdo N64,+rightx:b9,+righty:b3,-rightx:b4,-righty:b8,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,platform:Windows, +03000000c82d000012ab000000000000,8BitDo NES30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,  03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,  03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00002038000000000000,8Bitdo NES30 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, +03000000c82d00000751000000000000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b3,y:b4,platform:Windows,  03000000c82d00000360000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,  03000000c82d00002867000000000000,8BitDo S30 Modkit,a:b0,b:b1,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,  03000000c82d00000130000000000000,8BitDo SF30,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, @@ -34,6 +48,7 @@  03000000c82d00004028000000000000,8BitDo SN30,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,  03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,  03000000c82d00000351000000000000,8BitDo SN30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000021000000000000,8BitDo SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,  03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,  03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,  03000000c82d00000121000000000000,8BitDo SN30 Pro for 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, @@ -43,12 +58,19 @@  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, -03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, -03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, -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, -03000000c01100000355000011010000,ACRUX USB 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:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000fa190000f0ff000000000000,Acteck AGJ-3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000d81d00000e00000000000000,AC02,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows, +030000008f0e00001200000000000000,Acme GA02,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, +03000000c01100000355000000000000,Acrux,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000fa190000f0ff000000000000,Acteck AGJ 3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +030000006d0400000bc2000000000000,Action Pad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b8,lefttrigger:a5~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:a2~,start:b8,x:b3,y:b4,platform:Windows, +03000000d1180000402c000000000000,ADT1,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a3,rightx:a2,righty:a5,x:b3,y:b4,platform:Windows, +030000006f0e00001301000000000000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000006f0e00001302000000000000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000006f0e00001304000000000000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  030000006f0e00001413000000000000,Afterglow,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, +030000006f0e00003901000000000000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ab1200000103000000000000,Afterglow,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, +03000000ad1b000000f9000000000000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,  030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,  030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, @@ -56,68 +78,135 @@  030000006f0e00001402000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,  030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,  030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000d62000001d57000000000000,Airflo 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,  03000000491900001904000000000000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,  03000000710100001904000000000000,Amazon Luna Controller,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b8,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b4,rightstick:b7,rightx:a3,righty:a4,start:b6,x:b3,y:b2,platform:Windows, +03000000830500000160000000000000,Arcade,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b4,platform:Windows, +03000000120c0000100e000000000000,Armor 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000869800002500000000000000,Astro C40 TR PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, +03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,  03000000ef0500000300000000000000,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:Windows, +03000000fd0500000230000000000000,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:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows,  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, +03000000e4150000103f000000000000,Batarang,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  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, +03000000ad1b000001f9000000000000,BB 070,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  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, +03000000bc2000005250000000000000,Beitong G3,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a3,righty:a4,start:b15,x:b3,y:b4,platform:Windows, +030000000d0500000208000000000000,Belkin Nostromo N40,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b2,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, -03000000bc2000006321000000000000,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, +03000000bc2000006321000000000000,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,  03000000bc2000006412000000000000,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,  03000000c01100000555000000000000,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,  03000000c01100000655000000000000,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,  03000000790000000700000000000000,Betop 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:a4,start:b9,x:b3,y:b0,platform:Windows,  03000000808300000300000000000000,Betop 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:a4,start:b9,x:b3,y:b0,platform:Windows, +030000006f0e00006401000000000000,BF One,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,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows, +03000000300f00000202000000000000,Bigben,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a5,righty:a2,start:b7,x:b2,y:b3,platform:Windows, +030000006b1400000209000000000000,Bigben,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,  030000006b1400000055000000000000,Bigben PS3 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:Windows,  030000006b1400000103000000000000,Bigben PS3 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:b3,y:b2,platform:Windows, -03000000120c0000210e000000000000,Brook Mars,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, +03000000380700008232000000000000,Brawlpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000200e000000000000,Brook Mars 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:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000210e000000000000,Brook Mars 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:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,  0300000066f700000500000000000000,BrutalLegendTest,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:Windows, -03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, +03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, +030000006d04000042c2000000000000,ChillStream,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,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  03000000e82000006058000000000000,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:Windows,  03000000457500000401000000000000,Cobra,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, -030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),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, -030000005e040000a102000000000000,Controller (Xbox 360 Wireless Receiver for Windows),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, -030000005e040000ff02000000000000,Controller (Xbox One For Windows) - Wired,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, -030000005e040000ea02000000000000,Controller (Xbox One For Windows) - Wireless,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, +030000000b0400003365000000000000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows, +030000004c050000c505000000000000,CronusMax Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000d814000007cd000000000000,Cthulhu,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, +03000000d8140000cefa000000000000,Cthulhu,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,  03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows, +030000003807000002cb000000000000,Cyborg,a:b0,b:b1,back:b6,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:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  03000000a306000022f6000000000000,Cyborg V.3 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:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000f806000000a3000000000000,DA Leader,a:b7,b:b6,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b8,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:b3,rightx:a2,righty:a3,start:b12,x:b4,y:b5,platform:Windows, +030000001a1c00000001000000000000,Datel,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,  03000000451300000830000000000000,Defender Game Racer X7,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,  030000007d0400000840000000000000,Destroyer Tiltpad,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,x:b0,y:b3,platform:Windows, +03000000c0160000e105000000000000,Dual,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:a4,start:b9,x:b0,y:b3,platform:Windows,  03000000791d00000103000000000000,Dual Box WII,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:Windows, +030000007c1800000006000000000000,Dual Compat,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:Windows, +030000004f040000070f000000000000,Dual Power,a:b8,b:b9,back:b4,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,leftshoulder:b13,leftstick:b6,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b12,rightstick:b7,righttrigger:b15,start:b5,x:b10,y:b11,platform:Windows, +030000004f04000012b3000000000000,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, +030000004f04000020b3000000000000,Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,  03000000bd12000002e0000000000000,Dual USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows, +03000000ff1100003133000000000000,DualForce,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b1,platform:Windows,  030000008f0e00000910000000000000,DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows, -030000006f0e00003001000000000000,EA SPORTS 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, +03000000317300000100000000000000,DualShock 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +030000006f0e00003001000000000000,EA Sports 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, +03000000fc0400000250000000000000,Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +030000006e0500000a20000000000000,Elecom DUX60 MMO Gamepad,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows,  03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,  03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, +030000006e0500000520000000000000,Elecom P301U,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:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, +03000000411200004450000000000000,Elecom U1012,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:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, +030000006e0500000320000000000000,Elecom U3613M (DInput),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:Windows, +030000006e0500000e20000000000000,Elecom U3912T,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:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, +030000006e0500000f20000000000000,Elecom U4013S,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:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, +030000006e0500001320000000000000,Elecom U4113,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, +030000006e0500001020000000000000,Elecom U4113S,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, +030000006e0500000720000000000000,Elecom W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,  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, +03000000430b00000300000000000000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000242f000000b7000000000000,ESM 9110,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Windows, +03000000101c0000181c000000000000,Essential,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b4,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,  030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,  03000000341a00000108000000000000,EXEQ RF 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, -030000006f0e00008401000000000000,Faceoff Deluxe+ Audio Wired Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000790000003018000000000000,F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000242f00003900000000000000,F300 Elite,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, +030000006f0e00008401000000000000,Faceoff Deluxe Audio Wired Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,  030000006f0e00008001000000000000,Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000021000000090000000000000,FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +0300000011040000c600000000000000,FC801,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, +030000004f04000008d0000000000000,Ferrari 150,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000852100000201000000000000,FF GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,  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,  030000000d0f00008700000000000000,Fighting Stick mini 4,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,  030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,  030000000d0f00002700000000000000,FIGHTING STICK 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:Windows, +03000000380700002847000000000000,FightPad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b000028f0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b00002ef0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b000038f0000000000000,Fightpad TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, +03000000380700001847000000000000,FightStick,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, +03000000380700008031000000000000,FightStick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008731000000000000,FightStick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000003807000038b7000000000000,FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,  78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows, +03000000f806000001a3000000000000,Firestorm,a:b9,b:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b10,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b12,x:b8,y:b4,platform:Windows, +03000000b50700000399000000000000,Firestorm 2,a:b2,b:b4,back:b10,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,righttrigger:b9,start:b11,x:b3,y:b5,platform:Windows, +03000000b50700001302000000000000,Firestorm D3,a:b0,b:b2,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,x:b1,y:b3,platform:Windows, +03000000b40400001024000000000000,Flydigi Apex,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000151900004000000000000000,Flydigi Vader 2,a:b11,b:b10,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,leftstick:b1,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b0,righttrigger:b4,rightx:a3,righty:a4,start:b2,x:b9,y:b8,platform:Windows, +03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b4,paddle2:b5,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Windows, +030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b5,x:b2,y:b3,platform:Windows, +03000000ac0500002d02000000000000,G2U,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,  03000000790000002201000000000000,Game Controller for PC,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,  0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, +03000000341a000005f7000000000000,GameCube,a:b2,b:b3,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:b1,y:b0,platform:Windows, +03000000430b00000500000000000000,GameCube,a:b0,b:b2,dpdown:b10,dpleft:b8,dpright:b9,dpup:b11,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a3,rightx:a5,righty:a2,start:b7,x:b1,y:b3,platform:Windows, +03000000790000004718000000000000,GameCube,a:b1,b:b0,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:b2,y:b3,platform:Windows,  03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows, -03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, -030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,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, +03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows, +030000008f0e00000d31000000000000,Gamepad 3 Turbo,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,  03000000280400000140000000000000,GamePad Pro USB,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:Windows,  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, +030000004c0e00001035000000000000,Gamester,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +030000000d0f00001110000000000000,GameStick Bluetooth Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +0300000047530000616d000000000000,GameStop,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,  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, -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, +03000000b62500000100000000000000,Gametel GT004 01,a:b3,b:b0,dpdown:b10,dpleft:b9,dpright:b8,dpup:b11,leftshoulder:b4,rightshoulder:b5,start:b7,x:b1,y:b2,platform:Windows, +030000008f0e00001411000000000000,Gamo2 Divaller 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:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000a857000000000000,Gator Claw,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000c9110000f055000000000000,GC100XF,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,  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, @@ -127,72 +216,134 @@  03000000f025000021c1000000000000,Gioteck PS3 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,  03000000f0250000c383000000000000,Gioteck VX2 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,  03000000f0250000c483000000000000,Gioteck VX2 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, +030000004f04000026b3000000000000,GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +0300000079000000d418000000000000,GPD Win,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000c6240000025b000000000000,GPX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  030000007d0400000540000000000000,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:Windows, +030000007d0400000340000000000000,Gravis G44011 Xterminator,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a2,start:b9,x:b3,y:b4,platform:Windows, +030000008f0e00000610000000000000,GreenAsia,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a5,righty:a2,start:b11,x:b3,y:b0,platform:Windows, +03000000ac0500006b05000000000000,GT2a,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,  03000000341a00000302000000000000,Hama Scorpad,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, +03000000fd0500003902000000000000,Hammerhead,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,rightshoulder:b7,rightstick:b5,righttrigger:b9,start:b10,x:b0,y:b1,platform:Windows, +03000000fd0500002a26000000000000,Hammerhead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows, +03000000fd0500002f26000000000000,Hammerhead FX,a:b4,b:b5,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b1,y:b2,platform:Windows,  030000000d0f00004900000000000000,Hatsune Miku Sho 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, -030000001008000001e1000000000000,Havit HV-G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows, -03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, -03000000632500002605000000000000,HJD-X,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, +030000001008000001e1000000000000,Havit HV G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows, +030000000d0f00000c00000000000000,HEXT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000d81400000862000000000000,HitBox Edition Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, +03000000632500002605000000000000,HJD X,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, +030000000d0f00000a00000000000000,Hori DOA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000000d0f00008600000000000000,Hori Fighting Commander,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000000d0f0000ba00000000000000,Hori Fighting Commander,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,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  030000000d0f00002d00000000000000,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:Windows, -030000000d0f00005f00000000000000,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:Windows, -030000000d0f00005e00000000000000,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:Windows, +030000000d0f00005f00000000000000,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:Windows, +030000000d0f00005e00000000000000,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:Windows, +030000000d0f00005100000000000000,Hori Fighting 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:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00001000000000000000,Hori Fighting Stick,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, +030000000f0d00000010000000000000,Hori Fighting Stick 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00003200000000000000,Hori Fighting Stick 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000c000000000000000,Hori Fighting Stick 4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000000d0f00000d00000000000000,Hori Fighting Stick EX2,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +030000000d0f00003701000000000000,Hori Fighting Stick Mini,a:b1,b:b0,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,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Windows,  030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00002100000000000000,Hori Fighting Stick V3,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, +030000000d0f0000a000000000000000,Hori Grip TAC4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b13,x:b0,y:b3,platform:Windows, +030000000d0f00000101000000000000,Hori Mini Hatsune Miku FT,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,  030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,  030000000d0f00000900000000000000,Hori Pad 3 Turbo,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,  030000000d0f00004d00000000000000,Hori Pad A,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, +030000000d0f00003801000000000000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Windows,  030000000d0f00009200000000000000,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:Windows, -030000000d0f00001600000000007803,HORI Real Arcade Pro EX-SE (Xbox 360),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows, +030000000d0f00001100000000000000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00002600000000000000,Hori Real Arcade Pro 3P,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, +030000000d0f00004b00000000000000,Hori Real Arcade Pro 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006a00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006b00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008a00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008b00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006f00000000000000,Hori Real Arcade Pro 4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00007000000000000000,Hori Real Arcade Pro 4 VLX,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, +030000000d0f00003d00000000000000,Hori Real Arcade Pro N3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b10,leftstick:b4,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b6,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000ae00000000000000,Hori Real Arcade Pro N4,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,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000000d0f00008c00000000000000,Hori Real Arcade Pro P4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000000d0f0000aa00000000000000,Hori Real Arcade Pro S,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, +030000000d0f0000d800000000000000,Hori Real Arcade Pro S,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:Windows, +030000000d0f00002200000000000000,Hori 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,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00005b00000000000000,Hori Real Arcade Pro V4,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, +030000000d0f00005c00000000000000,Hori Real Arcade Pro V4,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, +030000000d0f0000af00000000000000,Hori Real Arcade Pro VHS,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:a5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00001b00000000000000,Hori Real Arcade Pro VX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b000002f5000000000000,Hori Real Arcade Pro VX,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b07,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b08,righttrigger:b11,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Windows,  030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000c900000000000000,Hori Taiko Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00002301000000000000,Hori Wired PS4 Controller Light,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,  030000000d0f0000c100000000000000,Horipad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00006e00000000000000,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:Windows, -030000000d0f00006600000000000000,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:Windows, +030000000d0f00006400000000000000,Horipad 3TP,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00001300000000000000,Horipad 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,  030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f0000ee00000000000000,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:Windows, -03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows, +030000000d0f00006e00000000000000,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:Windows, +030000000d0f00006600000000000000,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:Windows, +030000000d0f00004200000000000000,Horipad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000ad1b000001f5000000000000,Horipad EXT2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005b1c00002400000000000000,Horipad Mini,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows, +030000000d0f0000ee00000000000000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006700000000000000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000000d0f0000dc00000000000000,Horipad Switch,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  030000008f0e00001330000000000000,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:Windows,  03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,  03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -03000000830500006020000000000000,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:Windows, +030000005c0a00000285000000000000,iDroidCon,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b6,platform:Windows, +03000000696400006964000000000000,iDroidCon Bluetooth Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,  03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -030000006f0e00002401000000000000,INJUSTICE FightStick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -03000000ac0500002c02000000000000,IPEGA,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, -03000000491900000204000000000000,Ipega PG-9023,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, -03000000491900000304000000000000,Ipega PG-9087 - Bluetooth Gamepad,+righty:+a5,-righty:-a4,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,start:b11,x:b3,y:b4,platform:Windows, -030000006e0500000a20000000000000,JC-DUX60 ELECOM MMO Gamepad,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows, -030000006e0500000520000000000000,JC-P301U,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:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, -030000006e0500000320000000000000,JC-U3613M (DInput),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:Windows, -030000006e0500000720000000000000,JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, +030000006f0e00002401000000000000,Injustice FightStick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +03000000fd0500005302000000000000,InterAct ProPad,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Windows, +03000000ac0500002c02000000000000,Ipega Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000491900000204000000000000,Ipega PG9023,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, +03000000491900000304000000000000,Ipega PG9087 - Bluetooth Gamepad,+righty:+a5,-righty:-a4,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,start:b11,x:b3,y:b4,platform:Windows,  030000007e0500000620000000000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows, -030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,  030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, -030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, -03000000bd12000003c0000010010000,Joypad Alpha Shock,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, -03000000bd12000003c0000000000000,JY-P70UR,a:b1,b:b0,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b4,x:b3,y:b2,platform:Windows, +03000000bd12000003c0000000000000,Joypad Alpha Shock,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, +03000000250900000017000000000000,Joypad to USB Adapter,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows, +03000000ff1100004033000000000000,JPD FFB,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:a2,start:b15,x:b3,y:b0,platform:Windows,  03000000242f00002d00000000000000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,  03000000242f00008a00000000000000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, +03000000c4100000c082000000000000,KADE,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, +03000000828200000180000000000000,Keio,a:b4,b:b5,back:b8,leftshoulder:b2,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b9,x:b0,y:b1,platform:Windows,  03000000790000000200000000000000,King PS3 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:a4,start:b9,x:b3,y:b0,platform:Windows, +03000000bd12000001e0000000000000,Leadership,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, +030000006f0e00000103000000000000,Logic3,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000006f0e00000104000000000000,Logic3,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000008f0e00001300000000000000,Logic3,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,  030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,  030000006d040000d2ca000000000000,Logitech Cordless Precision,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,  030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,  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, +030000006d0400001dc2000000000000,Logitech F310,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,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000006d0400001ec2000000000000,Logitech F510,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,rightx:a3,righty:a4,start:b7,x:b2,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, +030000006d0400001fc2000000000000,Logitech F710,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,rightx:a3,righty:a4,start:b7,x:b2,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,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000006d04000009c2000000000000,Logitech WingMan,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,  030000006d0400000ac2000000000000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,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, -03000000380700008433000000000000,Mad Catz FightStick TE S+ (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, -03000000380700008483000000000000,Mad Catz FightStick TE S+ (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, -03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000380700006252000000000000,Mad Catz Micro 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, +03000000380700005645000000000000,Lynx,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000222200006000000000000000,Macally,a:b1,b:b2,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,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, +03000000380700003888000000000000,Mad Catz Arcade Fightstick TE S Plus 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:Windows, +03000000380700008532000000000000,Mad Catz Arcade Fightstick TE S 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:Windows, +03000000380700006352000000000000,Mad Catz CTRLR,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, +03000000380700006652000000000000,Mad Catz CTRLR,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, +03000000380700008433000000000000,Mad Catz FightStick TE S 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, +03000000380700008483000000000000,Mad Catz FightStick TE S 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, +03000000380700008134000000000000,Mad Catz FightStick TE2 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008184000000000000,Mad Catz FightStick TE2 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700006252000000000000,Mad Catz Micro CTRLR,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, +03000000380700001888000000000000,Mad Catz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000380700008081000000000000,Mad Catz SFV Arcade FightStick Alpha 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,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,  03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,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:Windows,  03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,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:Windows, -03000000380700008532000000000000,Madcatz Arcade Fightstick TE S 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:Windows, -03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ 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:Windows, -03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha 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,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,  030000002a0600001024000000000000,Matricom,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:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,  030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,  03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows, @@ -205,36 +356,73 @@  0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,  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, +0300000079000000ae18000000000000,Mega Drive,a:b0,b:b1,back:b7,dpdown:b14,dpleft:b15,dpright:b13,dpup:b2,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +03000000c0160000990a000000000000,Mega Drive,a:b0,b:b1,leftx:a0,lefty:a1,righttrigger:b2,start:b3,platform:Windows, +030000005e0400000300000000000000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, +030000005e0400000700000000000000,Microsoft SideWinder,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +030000005e0400000e00000000000000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows, +030000005e0400002700000000000000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Windows, +03000000280d00000202000000000000,Miller Lite Cantroller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b5,x:b2,y:b3,platform:Windows, +030000005b1c00002500000000000000,Mini,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows, +03000000ad1b000023f0000000000000,MLG,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a6,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +03000000ad1b00003ef0000000000000,MLG FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,  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, -03000000c62400001a89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, -03000000c62400001b89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000d6200000e589000000000000,Moga 2,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,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows, +03000000d62000007162000000000000,Moga Pro,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,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows, +03000000d6200000ad0d000000000000,Moga Pro,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,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000c62400002a89000000000000,Moga XP5A 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 XP5A 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, +03000000c62400001a89000000000000,Moga XP5X 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, +03000000c62400001b89000000000000,Moga XP5X 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, -03000000921200004b46000000000000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows, -03000000790000004518000000000000,NEXILUX GAMECUBE Controller Adapter,platform:Windows,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4, +03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,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, +030000006b1400001106000000000000,Nacon Revolution 3 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:Windows, +030000006b140000100d000000000000,Nacon Revolution Infinity 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:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006b140000080d000000000000,Nacon Revolution Unlimited Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000bd12000001c0000000000000,Nebular,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:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000eb0300000000000000000000,NeGcon USB Adapter,a:a2,b:b13,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,lefttrigger:a4,leftx:a1,righttrigger:b11,start:b3,x:a3,y:b12,platform:Windows, +0300000038070000efbe000000000000,NEO SE,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, +03000000921200004b46000000000000,NES 2 port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows, +03000000000f00000100000000000000,NES Controller,a:b1,b:b0,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows, +03000000571d00002100000000000000,NES Controller,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows, +03000000921200004346000000000000,NES Controller,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows, +03000000790000004518000000000000,NEXILUX 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:Windows,  030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Windows, +03000000050b00000045000000000000,Nexus,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:b10,x:b2,y:b3,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, +030000007e0500001920000000000000,Nintendo Switch N64 Controller,+rightx:b8,+righty:b2,-rightx:b3,-righty:b7,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b10,start:b9,platform:Windows, +030000007e0500001720000000000000,Nintendo Switch Online Controller,a:b0,b:b1,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:b2,y:b3,platform:Windows,  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:Windows,  030000000d0500000308000000000000,Nostromo N45,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:Windows, +03000000d620000013a7000000000000,NSW 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:Windows,  03000000550900001472000000000000,NVIDIA Controller v01.04,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,platform:Windows, -030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows, +03000000550900001072000000000000,NVIDIA Shield,a:b9,b:b8,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b3,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b2,righttrigger:a4,rightx:a2,righty:a5,start:b0,x:b7,y:b6,platform:Windows, +030000005509000000b4000000000000,NVIDIA Virtual Gamepad,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, +03000000120c00000288000000000000,Nyko Air Flo Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +030000004b120000014d000000000000,Nyko Airflo,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows, +03000000d62000001d57000000000000,Nyko Airflo 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,  03000000782300000a10000000000000,Onlive Wireless Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows, +030000000d0f00000401000000000000,Onyx,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,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000008916000001fd000000000000,Onza CE,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:a3,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000008916000000fd000000000000,Onza TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  03000000d62000006d57000000000000,OPP 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,  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, +030000006f0e00008501000000000000,PDP Fightpad Pro,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b0,platform:Windows,  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, +03000000d9040000160f000000000000,Playstation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,  030000004c0500003713000000000000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,  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, +030000006d04000084ca000000000000,Precision,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,  03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000c62400001a53000000000000,Pro Ex Mini,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  03000000d62000009f31000000000000,Pro Ex mini 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,  03000000d6200000c757000000000000,Pro Ex mini 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, +03000000120c0000110e000000000000,Pro5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,  03000000632500002306000000000000,PS Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,  03000000e30500009605000000000000,PS to USB convert cable,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,  03000000100800000100000000000000,PS1 Controller,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, @@ -244,122 +432,271 @@  03000000666600006706000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,  030000006b1400000303000000000000,PS2 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:Windows,  030000009d0d00001330000000000000,PS2 Controller,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:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000250900000088000000000000,PS2 Controller,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, +03000000250900006888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b6,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, +03000000120a00000100000000000000,PS3 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000120c00001307000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c00001cf1000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000f90e000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000250900000118000000000000,PS3 Controller,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, +03000000250900000218000000000000,PS3 Controller,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,  03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,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:b0,y:b3,platform:Windows,  030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,platform:Windows, +030000004f1f00000800000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,  03000000632500007505000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,  03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, +03000000888800000804000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,leftshoulder:b10,leftstick:b1,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows, +030000008f0e00000300000000000000,PS3 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:a5,start:b9,x:b3,y:b0,platform:Windows,  030000008f0e00001431000000000000,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, +03000000ba2200002010000000000000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Windows,  030000003807000056a8000000000000,PS3 RF 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:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000100000008200000000000000,PS360+ v1.66,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:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000100000008200000000000000,PS360 v1.66,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:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000120c00000807000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000111e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000121e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000130e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000150e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000180e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000181e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000191e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c00001e0e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000a957000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000aa57000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000f10e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000f21c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000f31c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000f41c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000f51c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120c0000f70e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000120e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000160e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000001a1e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,  030000004c050000a00b000000000000,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:Windows, -030000004c050000c405000000000000,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:Windows, +030000004c050000c405000000000000,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,touchpad:b13,x:b0,y:b3,platform:Windows,  030000004c050000cc09000000000000,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:Windows,  030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,  03000000ff000000cb01000000000000,PSP,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, -03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows, -03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, -03000000222c00000020000000000000,QANBA DRONE 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:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows, -03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, -03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows, -03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,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, -03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,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, +03000000830500005020000000000000,PSX,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:Windows, +03000000300f00000111000000000000,Qanba 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00000211000000000000,Qanba 2P,a:b1,b:b0,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, +03000000300f00000011000000000000,Qanba Arcade Stick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows, +03000000300f00001611000000000000,Qanba Arcade Stick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, +03000000222c00000020000000000000,Qanba Drone Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00001211000000000000,Qanba Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00001210000000000000,Qanba Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, +03000000341a00000104000000000000,Qanba Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows, +03000000222c00000223000000000000,Qanba Obsidian Arcade Stick 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, +03000000222c00000023000000000000,Qanba Obsidian Arcade Stick PS4,a:b1,b:b2,back:b13,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, +030000008a2400006682000000000000,R1 Mobile Controller,a:b3,b:b1,back:b7,leftx:a0,lefty:a1,start:b6,x:b4,y:b0,platform:Windows, +03000000086700006626000000000000,RadioShack,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, +030000009b2800002300000000000000,Raphnet Technologies 3DO USB Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows, +030000009b2800006900000000000000,Raphnet Technologies 3DO USB Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows, +030000009b2800000800000000000000,Raphnet Technologies Dreamcast USB Adapter,a:b2,b:b1,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,lefttrigger:a2,leftx:a0,righttrigger:a3,righty:a1,start:b3,x:b10,y:b9,platform:Windows, +030000009b2800003200000000000000,Raphnet Technologies 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,Raphnet Technologies 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, +030000009b2800001800000000000000,Raphnet Technologies Jaguar USB Adapter,a:b2,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b10,start:b3,x:b11,y:b12,platform:Windows, +030000009b2800000200000000000000,Raphnet Technologies NES USB Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,platform:Windows, +030000009b2800004300000000000000,Raphnet Technologies Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, +030000009b2800000500000000000000,Raphnet Technologies Saturn Adapter 2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, +030000009b2800000300000000000000,Raphnet Technologies SNES USB Adapter,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, +030000009b2800005600000000000000,Raphnet Technologies SNES USB Adapter,a:b1,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800005700000000000000,Raphnet Technologies SNES USB Adapter,a:b1,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800001e00000000000000,Raphnet Technologies Vectrex USB Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a1,lefty:a2,x:b2,y:b3,platform:Windows, +030000009b2800002b00000000000000,Raphnet Technologies Wii Classic USB Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800002c00000000000000,Raphnet Technologies Wii Classic USB Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,  03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -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, +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, +03000000321500000010000000000000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,  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, +03000000321500000710000000000000,Razer Raiju TE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000a10000000000000,Razer Raiju TE,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, +03000000321500000410000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000910000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,  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, -030000000d0f00008a00000000000000,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, -030000000d0f00008b00000000000000,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, -030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,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, -030000000d0f00002200000000000000,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,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00005b00000000000000,Real Arcade Pro.V4,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, -030000000d0f00005c00000000000000,Real Arcade Pro.V4,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, -03000000790000001100000000000000,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:Windows, -03000000bd12000013d0000000000000,Retrolink USB SEGA Saturn Classic,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows, +03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, +03000000830500006020000000000000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, +03000000bd12000013d0000000000000,Retrolink USB Sega Saturn Classic,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows, +03000000bd12000015d0000000000000,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,  0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,  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, +03000000830500000960000000000000,Revenger,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b5,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, +030000006b140000020d000000000000,Revolution Pro Controller 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, +030000006f0e00001f01000000000000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000006f0e00004601000000000000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000c6240000fefa000000000000,Rock Candy Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  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, -030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004f04000001d0000000000000,Rumble Force,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, +030000004f04000003d0000000000000,Run N Drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004f04000009d0000000000000,Run N Drive,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, +030000008916000000fe000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000c6240000045d000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  03000000a30600001af5000000000000,Saitek Cyborg,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:a4,start:b9,x:b0,y:b3,platform:Windows,  03000000a306000023f6000000000000,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:Windows,  03000000300f00001201000000000000,Saitek Dual Analog 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:Windows,  03000000a30600000701000000000000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Windows,  03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,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:b10,x:b0,y:b1,platform:Windows, +03000000a30600000d5f000000000000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows, +03000000a30600000dff000000000000,Saitek P2600,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:a5,righty:a2,start:b8,x:b0,y:b3,platform:Windows,  03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,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:Windows, +03000000a306000018f5000000000000,Saitek P3200,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:a4,start:b9,x:b0,y:b3,platform:Windows,  03000000300f00001001000000000000,Saitek P480 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:Windows, +03000000a30600000901000000000000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b8,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b5,rightx:a3,righty:a2,x:b0,y:b1,platform:Windows,  03000000a30600000b04000000000000,Saitek P990,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:a2,start:b9,x:b0,y:b3,platform:Windows, -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:Windows,  03000000a30600002106000000000000,Saitek PS1000,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:Windows,  03000000a306000020f6000000000000,Saitek PS2700,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:Windows,  03000000300f00001101000000000000,Saitek Rumble Pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, +03000000e804000000a0000000000000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c01100000252000000000000,Sanwa Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +03000000c01100004350000000000000,Sanwa Micro Grip P3,a:b1,b:b0,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,x:b3,y:b2,platform:Windows, +03000000411200004550000000000000,Sanwa Micro Grip Pro,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:a1,righty:a2,start:b9,x:b1,y:b3,platform:Windows, +03000000c01100004150000000000000,Sanwa Micro Grip Pro,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, +03000000c01100004450000000000000,Sanwa Online Grip,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b14,x:b3,y:b4,platform:Windows,  03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows, -0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, -030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, -03000000a30c00002500000000000000,Sega Genesis Mini 3B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows, -03000000a30c00002400000000000000,Sega Mega Drive Mini 6B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, -03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows, -03000000341a00000908000000000000,SL-6566,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, -030000008f0e00000800000000000000,SpeedLink Strike FX,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, +03000000830500006120000000000000,Sanwa Smart Grip II,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,x:b1,y:b3,platform:Windows, +03000000c01100000051000000000000,Satechi Bluetooth Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +030000004f04000028b3000000000000,Score A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows, +03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, +0300000000050000289b000000000000,Sega Saturn Adapter 2,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, +0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows, +03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, +030000003b07000004a1000000000000,SFX,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:Windows, +03000000120c00001c1e000000000000,SnakeByte GamePad 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +0300000003040000c197000000000000,SNES Controller,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, +03000000571d00002000000000000000,SNES Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, +0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows, +03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, +030000008b2800000300000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, +03000000921200004653000000000000,SNES Controller,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, +03000000341a00000208000000000000,Speedlink 6555,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows, +03000000341a00000908000000000000,Speedlink 6566,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, +030000008f0e00000800000000000000,Speedlink Strike FX,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,  03000000c01100000591000000000000,Speedlink Torid,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,  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, +03000000de280000fc11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000de280000ff11000000000000,Steam Virtual Gamepad,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, +03000000120c0000160e000000000000,Steel Play Metaltech PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,  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, +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, +03000000381000003014000000000000,Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000381000003114000000000000,Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000380700003847000000000000,Street Fighter Fighting Stick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,platform:Windows, +030000001f08000001e4000000000000,Super Famicom Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, +03000000790000000418000000000000,Super Famicom Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,  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, +030000000d0f0000f600000000000000,Switch Hori Pad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000457500002211000000000000,Szmy Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000004f0400000ab1000000000000,T16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows, +030000000d0f00007b00000000000000,TAC GEAR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000d814000001a0000000000000,TE Kitty,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,  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, +03000000ba2200000701000000000000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b2,platform:Windows, +03000000c61100001000000000000000,Tencent Xianyou Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows, +03000000790000002601000000000000,TGZ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,  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, +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, +030000006d04000088ca000000000000,Thunderpad,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,  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, +030000004f04000007d0000000000000,TMini 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,  03000000d62000006000000000000000,Tournament 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, +03000000c01100000055000000000000,Tronsmart,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,  030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,  03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,  030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows, +03000000411200000450000000000000,Twin Shock,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Windows,  03000000d90400000200000000000000,TwinShock PS2,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, -030000006e0500001320000000000000,U4113,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,  03000000101c0000171c000000000000,uRage 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:Windows, -03000000300f00000701000000000000,USB 4-Axis 12-Button 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:a2,start:b9,x:b3,y:b0,platform:Windows, -03000000341a00002308000000000000,USB gamepad,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, -030000005509000000b4000000000000,USB gamepad,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,platform:Windows, -030000006b1400000203000000000000,USB gamepad,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, -03000000790000000a00000000000000,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:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, -03000000f0250000c183000000000000,USB 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, -03000000ff1100004133000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows, -03000000632500002305000000000000,USB Vibration Joystick (BM),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, +030000000b0400003065000000000000,USB 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:a5,start:b9,x:b3,y:b0,platform:Windows, +03000000242f00006e00000000000000,USB Game Controller,a:b1,b:b4,back:b10,leftshoulder:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b3,platform:Windows, +03000000300f00000701000000000000,USB 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:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000341a00002308000000000000,USB Game 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:Windows, +03000000666600000188000000000000,USB Game Controller,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, +03000000666600000288000000000000,USB Game Controller,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, +030000006b1400000203000000000000,USB Game 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:Windows, +03000000790000000a00000000000000,USB 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:a4,start:b9,x:b3,y:b0,platform:Windows, +03000000b404000081c6000000000000,USB 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:a5,start:b9,x:b3,y:b0,platform:Windows, +03000000b50700001503000000000000,USB Game Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b0,y:b1,platform:Windows, +03000000bd12000012d0000000000000,USB Game Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, +03000000f0250000c183000000000000,USB Game 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, +03000000ff1100004133000000000000,USB Game Controller,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:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000632500002305000000000000,USB Vibration Joystick,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,  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, +03000000120c0000ab57000000000000,Warrior Joypad JS083,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000007e0500003003000000000000,WiiU Pro,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b6,leftstick:b11,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b12,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, +0300000032150000030a000000000000,Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000ff02000000000000,Wired Xbox One 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, +030000005e040000ea02000000000000,Wireless Xbox One 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, +0300000032150000140a000000000000,Wolverine,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000002e160000efbe000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,platform:Windows, +03000000380700001647000000000000,Xbox 360 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000380700002045000000000000,Xbox 360 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700002644000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a2,righty:a5,start:b8,x:b2,y:b3,platform:Windows, +03000000380700002647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000003807000026b7000000000000,Xbox 360 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000380700003647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a7,righty:a5,start:b7,x:b2,y:b3,platform:Windows, +030000005e0400001907000000000000,Xbox 360 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e0400008e02000000000000,Xbox 360 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, +030000005e0400009102000000000000,Xbox 360 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b000000fd000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b000001fd000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b000016f0000000000000,Xbox 360 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b00008e02000000000000,Xbox 360 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, +03000000c62400000053000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000c6240000fdfa000000000000,Xbox 360 Controller,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,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000a102000000000000,Xbox 360 Wireless Receiver,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,  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, +03000000120c00000a88000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +03000000120c00001088000000000000,Xbox 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:a5~,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000002a0600002000000000000000,Xbox Controller,a:b0,b:b1,back:b13,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b5,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b15,righttrigger:b7,rightx:a2,righty:a5,start:b12,x:b2,y:b3,platform:Windows, +03000000300f00008888000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:b13,dpleft:b10,dpright:b11,dpup:b12,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +03000000380700001645000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +03000000380700002645000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000380700003645000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +03000000380700008645000000000000,Xbox 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e0400000202000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +030000005e0400008502000000000000,Xbox Controller,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,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e0400008702000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +030000005e0400008902000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b10,leftstick:b8,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b9,righttrigger:b4,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +030000000d0f00006300000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e0400000c0b000000000000,Xbox One 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000d102000000000000,Xbox One 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000dd02000000000000,Xbox One 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000e002000000000000,Xbox One 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000e302000000000000,Xbox One 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000fd02000000000000,Xbox One 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000006f0e0000a802000000000000,Xbox One 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000006f0e0000c802000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000c62400003a54000000000000,Xbox One 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:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,  030000005e040000130b000000000000,Xbox Series 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,  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, -03000000450c00002043000000000000,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:Windows, -03000000ac0500005b05000000000000,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:Windows, +03000000450c00002043000000000000,Xeox Gamepad SL6556BK,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, +030000006f0e00000300000000000000,XGear,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:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000ac0500005b05000000000000,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:Windows,  03000000172700004431000000000000,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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,  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, -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, +03000000790000004f18000000000000,ZDT Android 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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,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,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:Mac OS X, @@ -369,13 +706,15 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  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, +03000000c82d000012ab000001000000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00002028000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,  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, -03000000c82d00004028000000010000,8Bitdo SN30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,platform:Mac OS X, +03000000c82d00004028000000010000,8Bitdo SN30 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,  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, @@ -385,42 +724,46 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  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, +03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X, +03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,  03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,  03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,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,  03000000ef0500000300000000020000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X, -03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X, -03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,  03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,  03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,  03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000120c0000200e000000010000,Brook Mars,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, -03000000120c0000210e000000010000,Brook Mars,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, +03000000120c0000200e000000010000,Brook Mars 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:Mac OS X, +03000000120c0000210e000000010000,Brook Mars 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: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,  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, +03000000d8140000cecf000000000000,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,  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, +030000000d0f00008400000000010000,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: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, +030000000d0f00008500000000010000,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:Mac OS X, +03000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b4,paddle2:b5,paddle3:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Mac OS X,  03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X, -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, +03000000ad1b000001f9000000000000,Gamestop BB070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,  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,  03000000280400000140000000020000,Gravis Gamepad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, -030000008f0e00000300000007010000,GreenAsia Inc. USB Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X, +030000008f0e00000300000007010000,GreenAsia USB Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,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, -030000000d0f00005f00000000010000,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:Mac OS X, -030000000d0f00005e00000000010000,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:Mac OS X, -030000000d0f00005f00000000000000,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,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, -030000000d0f00005e00000000000000,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,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, -030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00005f00000000000000,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,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, +030000000d0f00005f00000000010000,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:Mac OS X, +030000000d0f00005e00000000000000,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,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, +030000000d0f00005e00000000010000,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:Mac OS X, +030000000d0f00004d00000000000000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,  030000000d0f00009200000000010000,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:Mac OS X, -030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00006600000000000000,HORIPAD FPS PLUS 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:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f0000ee00000000010000,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:Mac OS X, +030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00006600000000000000,Horipad FPS Plus 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:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f0000ee00000000010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,  030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X, -03000000830500006020000000010000,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:Mac OS X, -03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, +03000000830500006020000000000000,iBuffalo Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,  030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X,  030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,  03000000242f00002d00000007010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, @@ -429,7 +772,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  030000006d04000016c2000014040000,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:Mac OS X,  030000006d04000016c2000000000000,Logitech F310 Gamepad (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:Mac OS X,  030000006d04000018c2000000000000,Logitech F510 Gamepad (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:Mac OS X, -030000006d04000019c2000005030000,Logitech F710,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, +030000006d04000019c2000005030000,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:Mac OS X,  030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),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,  030000006d04000018c2000000010000,Logitech RumblePad 2 USB,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,  030000006d04000019c2000000000000,Logitech Wireless Gamepad (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:Mac OS X, @@ -437,7 +780,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  03000000380700005082000000010000,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:Mac OS X,  03000000380700008433000000010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,  03000000380700008483000000010000,Mad Catz FightStick TE S+ (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, -03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,x:b3,y:b0,back:b8,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:Mac OS X, +03000000790000000600000007010000,Marvo GT-004,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:Mac OS X,  03000000790000004418000000010000,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:Mac OS X,  03000000242f00007300000000020000,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:Mac OS X,  0300000079000000d218000026010000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, @@ -445,21 +788,20 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,  03000000790000000018000000010000,Mayflash Wii U Pro Controller Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,  03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, -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, +030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,  03000000d62000007162000001000000,Moga Pro 2 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, +03000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c62400002b89000000010000,MOGA XP5A 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,  03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Mac OS X,  030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X, -03000000d620000011a7000000020000,Nintendo Switch Core (Plus) 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, +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,  03000000d620000011a7000010050000,Nintendo Switch PowerA 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, +030000008f0e00000300000000000000,Piranha Xtreme PS3 Controller,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,  030000004c0500003713000000010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,  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, @@ -472,15 +814,17 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  030000004c050000cc09000000010000,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:Mac OS X,  050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,  030000008916000000fd000000000000,Razer Onza TE,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, -03000000321500000204000000010000,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:Mac OS X, -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, +03000000321500000204000000010000,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:Mac OS X, +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, +03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000790000001100000005010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000830500006020000000010000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,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, @@ -498,10 +842,12 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  03000000110100002014000000000000,SteelSeries Nimbus,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,  03000000110100002014000001000000,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,  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, +05000000484944204465766963650000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,  050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X, +05000000556e6b6e6f776e2048494400,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,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, +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, @@ -513,7 +859,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  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, +030000005e0400008e02000000000000,Xbox 360 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,  030000006f0e00000104000000000000,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,  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, @@ -524,28 +870,33 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  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,  030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,  030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000200b000011050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,  030000005e040000e002000000000000,Xbox 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:Mac OS X,  030000005e040000e002000003090000,Xbox 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:Mac OS X,  030000005e040000ea02000000000000,Xbox Wireless 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,  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, +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 +03000000021000000090000011010000,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,  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,  05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000008000000210000011010000,8BitDo NES30,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:Linux,  03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,  05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,  03000000022000000090000011010000,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:Linux, +03000000c82d00000190000011010000,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:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,  05000000203800000900000000010000,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:Linux,  05000000c82d00002038000000010000,8Bitdo NES30 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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, -03000000c82d00000190000011010000,8Bitdo NES30 Pro 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:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,  05000000c82d00000060000000010000,8BitDo SF30 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,  05000000c82d00000061000000010000,8Bitdo SF30 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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +030000003512000021ab000010010000,8BitDo SFC30,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,start:b11,x:b4,y:b3,platform:Linux,  03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,  030000003512000012ab000010010000,8Bitdo SFC30 GamePad,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,  05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, @@ -566,12 +917,12 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  050000005e040000e002000030110000,8BitDo Zero 2 (XInput),a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,  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, -03000000c01100000355000011010000,ACRUX USB 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:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,  030000006f0e00001302000000010000,Afterglow,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,  030000006f0e00003901000020060000,Afterglow Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  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, +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:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,  03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, @@ -579,80 +930,86 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  03000000790000003018000011010000,Arcade Fightstick F300,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,start:b9,x:b0,y:b3,platform:Linux,  03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,  03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,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, +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,  03000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux,  05000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux, -03000000503200000210000000000000,Atari Game 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:b3,y:b2,platform:Linux, +03000000503200000210000000000000,Atari Game Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux,  05000000503200000210000000000000,Atari Game 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:b3,y:b2,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,  03000000ef0500000300000000010000,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, -03000000c62400001b89000011010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c62400001b89000011010000,BDA MOGA XP5X 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,  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, -03000000c21100000791000011010000,Be1 GC101 Controller 1.03 mode,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, -03000000c31100000791000011010000,Be1 GC101 GAMEPAD 1.03 mode,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, -030000005e0400008e02000003030000,Be1 GC101 Xbox 360 Controller mode,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000666600006706000000010000,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, -03000000120c0000200e000011010000,Brook Mars,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, -03000000120c0000210e000011010000,Brook Mars,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, +03000000c21100000791000011010000,Be1 GC101 Controller 1.03,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, +03000000c31100000791000011010000,Be1 GC101 GAMEPAD 1.03,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, +030000005e0400008e02000003030000,Be1 GC101 Xbox 360,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, +05000000bc2000000055000001000000,BETOP AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000006b1400000209000011010000,Bigben,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, +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, +03000000120c0000200e000011010000,Brook Mars 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, +03000000120c0000210e000011010000,Brook Mars 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:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,  03000000120c0000f70e000011010000,Brook Universal Fighting Board,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: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,  030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux,  03000000260900008888000000010000,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:Linux, -03000000a306000022f6000011010000,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:Linux, -03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, -03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,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, +03000000a306000022f6000011010000,Cyborg V3 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:Linux,  030000004f04000004b3000010010000,Dual Power 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,  030000006f0e00003001000001010000,EA Sports 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, -03000000341a000005f7000010010000,GameCube {HuiJia USB box},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, +03000000c11100000191000011010000,EasySMX,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,Elecom U3613M,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, +03000000430b00000300000000010000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +03000000b40400001124000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,  03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000ac0500002d0200001b010000,Gamesir G4s,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b33,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000bc2000005656000011010000,Gamesir T4w,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,  0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,  030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000008f0e00000800000010010000,Gasia Co. Ltd PS(R) 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, -030000006f0e00001304000000010000,Generic X-Box pad,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, +030000008f0e00000800000010010000,Gasia PlayStation 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,  03000000451300000010000010010000,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:Linux, -03000000f0250000c183000010010000,Goodbetterbest Ltd USB 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, +03000000f0250000c183000010010000,Goodbetterbest Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,  0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  030000007d0400000540000000010000,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:Linux, -03000000280400000140000000010000,Gravis GamePad Pro USB ,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:Linux, -030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux, -030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,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:Linux, +03000000280400000140000000010000,Gravis GamePad Pro USB,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:Linux, +030000008f0e00000610000000010000,GreenAsia Electronics Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux, +030000008f0e00001200000010010000,GreenAsia USB Joystick,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:Linux,  0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,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,  03000000f0250000c383000010010000,GT VX2,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,  06000000adde0000efbe000002010000,Hidromancer Game 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, -03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, -03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -03000000632500002605000010010000,HJD-X,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:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, -030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux, -030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -030000000d0f0000c100000011010000,HORI CO. LTD. HORIPAD S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -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, +03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, +03000000c9110000f055000011010000,HJC Game Gamepqd,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, +03000000632500002605000010010000,HJDX,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:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000000d0f00000d00000000010000,Hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,rightshoulder:b7,start:b9,x:b1,y:b2,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, +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, +030000000d0f00005001000009040000,Hori Fighting Commander OCTA,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, +030000000d0f00001000000011010000,Hori Fighting Stick 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,  03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000000d0f00003801000011010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Linux,  030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro 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, -030000000d0f0000d800000072056800,HORI Real Arcade Pro S,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: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, +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, +030000000d0f00002200000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006a00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, +030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f0000ee00000011010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006700000001010000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000000d0f0000c100000011010000,Horipad S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000341a000005f7000010010000,HuiJia GameCube Controller Adpater,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,  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, +050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, +03000000d80400008200000003000000,IMS PCU0 Gamepad,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 I73000,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, +0500000049190000030400001b010000,Ipega PG9099,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, +05000000491900000204000000000000,Ipega PG9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,  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,  03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, @@ -664,7 +1021,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  03000000242f00002d00000011010000,JYS Wireless Adapter,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,  03000000242f00008a00000011010000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,  030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d040000d1ca000000000000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,  030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,  030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,  030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, @@ -673,62 +1030,68 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),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,  030000006d04000019c2000011010000,Logitech F710 Gamepad (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:Linux,  030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),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, -030000006d0400000ac2000010010000,Logitech Inc. WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Linux,  030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,  030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux, -050000004d4f435554452d3035305800,M54-PC,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, -05000000380700006652000025010000,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:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700005032000011010000,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:Linux, -03000000380700005082000011010000,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:Linux, +030000006d0400000ac2000010010000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Linux, +05000000380700006652000025010000,Mad Catz CTRLR,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, +03000000380700005032000011010000,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:Linux, +03000000380700005082000011010000,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:Linux,  03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, -03000000380700008034000011010000,Mad Catz fightstick (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, -03000000380700008084000011010000,Mad Catz fightstick (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, -03000000380700008433000011010000,Mad Catz FightStick TE S+ (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, -03000000380700008483000011010000,Mad Catz FightStick TE S+ (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, +03000000380700008034000011010000,Mad Catz fightstick 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, +03000000380700008084000011010000,Mad Catz fightstick 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, +03000000380700008433000011010000,Mad Catz FightStick TE S 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, +03000000380700008483000011010000,Mad Catz FightStick TE S 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,  03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,  03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,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,  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, -03000000242f0000f700000001010000,Magic-S Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  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:b0,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: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, +03000000242f0000f700000001010000,Mayflash Magic S Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  0300000025090000e803000001010000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,  03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,  030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, -030000005e0400008e02000004010000,Microsoft X-Box 360 pad,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, -030000005e0400008e02000062230000,Microsoft X-Box 360 pad,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, -050000005e040000050b000003090000,Microsoft X-Box One Elite 2 pad,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, -030000005e040000e302000003020000,Microsoft X-Box One Elite pad,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, -030000005e040000d102000001010000,Microsoft X-Box One pad,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, -030000005e040000dd02000003020000,Microsoft X-Box One pad (Firmware 2015),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, -030000005e040000d102000003020000,Microsoft X-Box One pad v2,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, -030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, -030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),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, -030000005e040000000b000008040000,Microsoft Xbox One Elite 2 pad - Wired,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, -030000005e040000ea02000008040000,Microsoft Xbox One S pad - Wired,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, -03000000c62400001a53000000010000,Mini PE,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, +030000005e0400000700000000010000,Microsoft SideWinder Game Pad USB,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux, +030000005e0400002700000000010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Linux, +030000005e0400008e02000056210000,Microsoft X-Box 360 pad,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, +030000005e0400008e02000004010000,Microsoft Xbox 360 pad,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, +030000005e0400008e02000062230000,Microsoft Xbox 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000000b000008040000,Microsoft Xbox One Elite 2 pad,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, +050000005e040000050b000003090000,Microsoft Xbox One Elite 2 pad,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000005e040000e302000003020000,Microsoft Xbox One Elite pad,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, +030000005e040000d102000001010000,Microsoft Xbox One pad,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, +030000005e040000dd02000003020000,Microsoft Xbox One pad 2015,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, +030000005e040000d102000003020000,Microsoft Xbox One pad v2,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, +030000005e040000ea02000008040000,Microsoft Xbox One S pad,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, +030000005e0400008502000000010000,Microsoft 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, +030000005e0400008902000021010000,Microsoft Xbox pad v2,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,  03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, +050000004d4f435554452d3035335800,Mocute 053X,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, +050000004d4f435554452d3035305800,Mocute 054X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,  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, -05000000c62400001a89000000010000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform: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, +03000000c62400002b89000011010000,MOGA XP5A 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 XP5A 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, +05000000c62400001a89000000010000,MOGA XP5X 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, +03000000250900006688000000010000,MP8866 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, -03000000790000004518000010010000,NEXILUX GAMECUBE Controller Adapter,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,platform:Linux, +030000004f1f00000800000011010000,Neogeo 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,start:b9,x:b0,y:b3,platform:Linux, +03000000790000004518000010010000,Nexilux 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,  030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux,  060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,  060000007e0500000820000000000000,Nintendo Combined Joy-Cons (joycond),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,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,  03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux, +050000004c69632050726f20436f6e00,Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,  050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b9,b:b8,back:b5,leftshoulder:b2,leftstick:b6,leftx:a1,lefty:a0~,rightshoulder:b4,start:b0,x:b7,y:b10,platform:Linux, +03000000d620000013a7000011010000,Nintendo Switch PowerA 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:Linux,  030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,  050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,  050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, @@ -741,34 +1104,35 @@ 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, +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,  03000000c0160000dc27000001010000,OnyxSoft Dual JoyDivision,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,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,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, -030000006f0e0000b802000013020000,PDP AFTERGLOW Wired 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, +030000006f0e0000b802000001010000,PDP Afterglow Wired 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, +030000006f0e0000b802000013020000,PDP Afterglow Wired 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,  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,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,  030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00008001000011010000,PDP Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,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, -05000000491900000204000000000000,PG-9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000006f0e0000a702000023020000,PDP Xbox One Raven Black,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  030000004c050000da0c000011010000,Playstation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, +03000000d9040000160f000000010000,Playstation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,  030000004c0500003713000011010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,  03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c62400001a53000000010000,PowerA Mini Pro Ex,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  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,  03000000d62000000228000001010000,PowerA Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000c62400001a58000001010000,PowerA Xbox One Cabled,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d62000000220000001010000,PowerA Wired Controller for Xbox One and Xbox Series S and X,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, +03000000c62400001a58000001010000,PowerA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  03000000c62400001a54000001010000,PowerA Xbox One Mini Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  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, @@ -802,18 +1166,21 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,  050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,  03000000ff000000cb01000010010000,PSP,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,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, +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, +03000000300f00001210000010010000,Qanba Joystick Plus,a:b0,b:b1,back:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,start:b9,x:b2,y:b3,platform:Linux,  030000009b2800004200000001010000,Raphnet Technologies Dual NES to USB v2.0,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start: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: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,  03000000321500000810000011010000,Razer Panthera Evo Arcade Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger: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, +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,  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, +05000000321500000a10000001000000,Razer Raiju Tournament Edition,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000710000000010000,Razer Raiju Tournament Edition Wired,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000010000011010000,Razer Rainu,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,  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, @@ -821,36 +1188,39 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,  050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,  0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000790000001100000010010000,Retrolink SNES 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:Linux, +03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Linux,  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, +03000000c6240000fefa000000010000,Rock Candy Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  03000000a306000023f6000011010000,Saitek Cyborg 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 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,  03000000a30600000701000000010000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Linux,  03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,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:b10,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, +03000000a306000018f5000010010000,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,  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,  03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,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,x:b0,y:b1,platform:Linux,  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,  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, -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, -03000000632500007505000010010000,SHANWAN PS3/PC 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, -03000000bc2000000055000010010000,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, -030000005f140000c501000010010000,SHANWAN Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000a30c00002500000011010000,Sega Genesis Mini 3B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux, +030000001f08000001e4000010010000,SFC Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,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, +03000000632500007505000010010000,Shanwan PS3 PC 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, +03000000bc2000000055000010010000,Shanwan PS3 PC Wired GamePad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000005f140000c501000010010000,Shanwan Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,  03000000632500002305000010010000,ShanWan 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:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, -03000000341a00000908000010010000,SL-6566,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, +03000000341a00000908000010010000,SL6566,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,  030000004c050000e60c000011810000,Sony DualSense,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, -050000004c050000e60c000000810000,Sony DualSense ,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, +050000004c050000e60c000000810000,Sony DualSense,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,  03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,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, -030000005e0400008e02000073050000,Speedlink TORID Wireless 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, -030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,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, +030000005e0400008e02000073050000,Speedlink Torid Wireless 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, +030000005e0400008e02000020200000,SpeedLink Xeox Pro Analog Gamepad pad,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,  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,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,  03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, @@ -870,13 +1240,14 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  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, -030000008f0e00000d31000010010000,SZMY-POWER CO. LTD. GAMEPAD 3 TURBO,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, -030000008f0e00001431000010010000,SZMY-POWER CO. LTD. PS3 gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000008f0e00000d31000010010000,SZMY Power 3 Turbo,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, +03000000457500002211000010010000,SZMY Power 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, +030000008f0e00001431000010010000,SZMY Power PS3 gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000ba2200000701000001010000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Linux,  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, -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, +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, +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,  030000004f04000003b3000010010000,Thrustmaster Firestorm Dual Analog 2,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b9,rightx:a2,righty:a3,x:b1,y:b3,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, @@ -887,49 +1258,65 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  030000004f04000007d0000000010000,Thrustmaster 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:Linux,  030000004f04000012b3000010010000,Thrustmaster vibrating gamepad,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,  03000000bd12000015d0000010010000,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:Linux, -03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, +03000000d814000007cd000011010000,Toodles 2008 Chimp PC PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,  030000005e0400008e02000070050000,Torid,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,  03000000c01100000591000011010000,Torid,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,  03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,  03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,  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, +03000000790000001100000011010000,USB Saturn Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux, +03000000790000002201000011010000,USB Saturn Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, +03000000b40400000a01000000010000,USB Saturn Pad,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,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, +05000000ac0500003232000001000000,VR Box Controller,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, -030000005e0400009102000007010000,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, -030000005e040000a102000000010000,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, -030000005e040000a102000007010000,X360 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, +030000005e0400008e02000010010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000014010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, -030000005e040000a102000014010000,Xbox 360 Wireless Receiver (XBOX),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, -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, +030000005e0400001907000000010000,Xbox 360 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, +030000005e0400009102000007010000,Xbox 360 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, +030000005e040000a102000000010000,Xbox 360 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, +030000005e040000a102000007010000,Xbox 360 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, +030000005e0400008e02000000010000,Xbox 360 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, +030000005e040000a102000014010000,Xbox 360 Wireless Receiver,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00001304000000010000,Xbox 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, +03000000ffff0000ffff000000010000,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, +0000000058626f782047616d65706100,Xbox 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:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400000a0b000005040000,Xbox One Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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,  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, +050000005e040000e302000002090000,Xbox One Elite,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,  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, +060000005e040000120b000007050000,Xbox One Wireless Controller (Model 1914),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, +030000005e0400000202000000010000,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,  030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000005050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,  030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,  050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,  050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, -030000005e040000120b000005050000,XBox Series pad,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, +03000000450c00002043000010010000,XEOX Gamepad SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +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, +03000000c0160000e105000001010000,XinMo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,  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, -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, +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, +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 +38653964633230666463343334313533,8BitDo Adapter,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +34343439373236623466343934376233,8BitDo FC30 Pro,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b28,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b29,righttrigger:b7,start:b5,x:b30,y:b2,platform:Android, +33656266353630643966653238646264,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,start:b10,x:b19,y:b2,platform:Android, +39366630663062373237616566353437,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,start:b6,x:b2,y:b3,platform:Android, +64653533313537373934323436343563,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,start:b6,x:b2,y:b3,platform:Android, +66356438346136366337386437653934,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,start:b18,x:b19,y:b2,platform:Android, +66393064393162303732356665666366,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,platform:Android,  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, @@ -938,60 +1325,151 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  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, +38426974646f20534633302050726f00,8BitDo SF30 Pro,a:b1,b:b0,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b17,platform:Android, +61623334636338643233383735326439,8BitDo SFC30,a:b0,b:b1,back:b4,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b31,start:b5,x:b30,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, +35383531346263653330306238353131,8BitDo SN30 PP,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  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, +36653638656632326235346264663661,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +38303232393133383836366330346462,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +38346630346135363335366265656666,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +66306331643531333230306437353936,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x: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, +33666663316164653937326237613331,8BitDo Zero,a:b0,b:b1,back:b15,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,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, -38383337343564366131323064613561,Brook Mars,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +33663434393362303033616630346337,8BitDo Zero 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +34656330626361666438323266633963,8BitDo Zero 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, +63396666386564393334393236386630,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +63633435623263373466343461646430,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +32333634613735616163326165323731,Amazon Luna Game Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, +38383337343564366131323064613561,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +33323763323132376537376266393366,Dual Strike,a:b24,b:b23,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,platform:Android, +30363230653635633863366338623265,Evo VR,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,x:b2,y:b3,platform:Android, +05000000b404000011240000dfff3f00,Flydigi Vader 2,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,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, +34323662653333636330306631326233,Google Nexus,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35383633353935396534393230616564,Google Stadia Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android,  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, +66633030656131663837396562323935,Hori Battle,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +35623466343433653739346434636330,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +65656436646661313232656661616130,Hori PC Engine Mini Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b18,platform:Android, +31303433326562636431653534636633,Hori Real Arcade Pro 3,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b2,y:b3,platform:Android, +64306137363261396266353433303531,InterAct GoPad,a:b24,b:b25,leftshoulder:b23,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,x:b21,y:b22,platform:Android, +65346535636333663931613264643164,Joy Con,a:b21,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b23,y:b24,platform:Android, +33346566643039343630376565326335,Joy Con (L),a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, +35313531613435623366313835326238,Joy Con (L),a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, +38383665633039363066383334653465,Joy Con (R),a:b0,b:b1,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +39363561613936303237333537383931,Joy Con (R),a:b0,b:b1,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +39656136363638323036303865326464,JYS Aapter,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +63316564383539663166353034616434,JYS Adapter,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android, +64623163333561643339623235373232,Logitech F310,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35623364393661626231343866613337,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +64396331333230326333313330336533,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +30363066623539323534363639323363,Magic NS,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +31353762393935386662336365626334,Magic NS,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +39623565346366623931666633323530,Magic NS,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android, +32303165626138343962363666346165,Mars,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +31323564663862633234646330373138,Mega Drive,a:b23,b:b22,leftx:a0,lefty:a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android, +37333564393261653735306132613061,Mega Drive,a:b21,b:b22,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +64363363336633363736393038313464,Mega Drive,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Android, +30306461613834333439303734316539,Microsoft SideWinder Pro,a:b0,b:b1,leftshoulder:b20,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b19,righttrigger:b10,start:b17,x:b2,y:b3,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, +32386235353630393033393135613831,Microsoft Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  7573622067616d657061642020202020,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform: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, +34323437396534643531326161633738,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  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, +61653962353232366130326530363061,Pokken,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,rightshoulder:b20,righttrigger:b10,start:b18,x:b0,y:b2,platform:Android, +32666633663735353234363064386132,PS2,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a3,righty:a2,start:b30,x:b24,y:b21,platform:Android, +61363034663839376638653463633865,PS3,a:b0,b:b1,back:b15,dpdown:a14,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +66366539656564653432353139356536,PS3,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, +66383132326164626636313737373037,PS3,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,  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, +30303839663330346632363232623138,PS4,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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, +31326235383662333266633463653332,PS4,a:b1,b:b16,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b17,x:b0,y:b2,platform:Android, +31663838336334393132303338353963,PS4,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +34613139376634626133336530386430,PS4,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +37626233336235343937333961353732,PS4,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38393161636261653636653532386639,PS4,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +63313733393535663339656564343962,PS4,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +63393662363836383439353064663939,PS4,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +65366465656364636137653363376531,PS4,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +66613532303965383534396638613230,PS4,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android,  050000004c050000c405000000783f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,  050000004c050000c4050000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  050000004c050000cc090000fffe3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +31373231336561636235613666323035,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  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,  050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +62653335326261303663356263626339,PSX,a:b19,b:b1,back:b17,leftshoulder:b9,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b20,start:b18,x:b2,y:b0,platform:Android, +64336263393933626535303339616332,Qanba 4RAF,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +36626666353861663864336130363137,Razer Junglecat,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  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, +61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,platform:Android, +38653130373365613538333235303036,Retroid Pocket 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +64363363336633363736393038313463,Retrolink,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b6,platform:Android, +33373336396634316434323337666361,RumblePad 2,a:b22,b:b23,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b24,platform:Android, +66386565396238363534313863353065,Sanwa Mobile,a:b21,b:b22,leftshoulder:b23,leftx:a0,lefty:a1,rightshoulder:b24,platform:Android, +32383165316333383766336338373261,Saturn,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, +37316565396364386635383230353365,Saturn,a:b21,b:b22,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +38613865396530353338373763623431,Saturn,a:b0,b:b1,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b19,start:b17,x:b2,y:b3,platform:Android, +61316232336262373631343137633631,Saturn,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, +30353835333338613130373363646337,SG H510,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +66386262366536653765333235343634,SG H510,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, +66633132393363353531373465633064,SG H510,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +62653761636366393366613135366338,SN30 PP,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38376662666661636265313264613039,SNES,a:b0,b:b1,back:b9,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android,  32633532643734376632656664383733,Sony DualSense,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android,  61303162353165316365336436343139,Sony DualSense,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +63303964303462366136616266653561,Sony PSP,a:b21,b:b22,back:b27,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b23,y:b24,platform:Android, +63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a3,righty:a4,start:b18,x:b0,y:b2,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, +0500000011010000201400000f7e0f00,SteelSeries Nimbus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,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, +30623739343039643830333266346439,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b23,paddle2:b24,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +31643365666432386133346639383937,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b23,paddle2:b24,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +30386438313564306161393537333663,Wii Classic,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, +33333034646336346339646538643633,Wii Classic,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,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, +30396232393162346330326334636566,Xbox 360,a:b0,b:b1,back:b4,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, +38313038323730383864666463383533,Xbox 360,a:b0,b:b1,back:b4,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, +65353331386662343338643939643636,Xbox 360,a:b0,b:b1,back:b4,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, +65613532386633373963616462363038,Xbox 360,a:b0,b:b1,back:b4,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,  050000005e0400008e02000000783f00,Xbox 360 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, +33356661323266333733373865656366,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35623965373264386238353433656138,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  050000005e040000000b000000783f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,  050000005e040000e002000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  050000005e040000ea02000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  050000005e040000fd020000ff7f3f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  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, +050000005e040000fd020000ffff3f00,Xbox One Wireless 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,  050000005e040000120b000000783f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,  050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  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, +36616131643361333337396261666433,Xbox Wireless Controller,a:b0,b:b1,back:b15,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,  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 @@ -1017,5 +1495,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,  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:iOS,  050000005e040000050b0000df070001,Xbox Elite Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b10,paddle2:b12,paddle3:b11,paddle4:b13,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,  050000005e040000050b0000ff070001,Xbox Elite Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, +050000005e040000130b0000df870001,Xbox Series X Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b10,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, +050000005e040000130b0000ff870001,Xbox Series X Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,  050000005e040000e0020000df070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,  050000005e040000e0020000ff070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, diff --git a/core/input/input.cpp b/core/input/input.cpp index 9195f7d8b5..3dfe73ab8e 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -35,11 +35,7 @@  #include "core/input/input_map.h"  #include "core/os/os.h" -#ifdef TOOLS_ENABLED -#include "editor/editor_settings.h" -#endif - -static const char *_joy_buttons[JOY_BUTTON_SDL_MAX] = { +static const char *_joy_buttons[(size_t)JoyButton::SDL_MAX] = {  	"a",  	"b",  	"x", @@ -63,7 +59,7 @@ static const char *_joy_buttons[JOY_BUTTON_SDL_MAX] = {  	"touchpad",  }; -static const char *_joy_axes[JOY_AXIS_SDL_MAX] = { +static const char *_joy_axes[(size_t)JoyAxis::SDL_MAX] = {  	"leftx",  	"lefty",  	"rightx", @@ -95,6 +91,7 @@ Input::MouseMode Input::get_mouse_mode() const {  void Input::_bind_methods() {  	ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed); +	ClassDB::bind_method(D_METHOD("is_physical_key_pressed", "keycode"), &Input::is_physical_key_pressed);  	ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed);  	ClassDB::bind_method(D_METHOD("is_joy_button_pressed", "device", "button"), &Input::is_joy_button_pressed);  	ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "exact_match"), &Input::is_action_pressed, DEFVAL(false)); @@ -106,7 +103,6 @@ void Input::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_vector", "negative_x", "positive_x", "negative_y", "positive_y", "deadzone"), &Input::get_vector, DEFVAL(-1.0f));  	ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false));  	ClassDB::bind_method(D_METHOD("remove_joy_mapping", "guid"), &Input::remove_joy_mapping); -	ClassDB::bind_method(D_METHOD("joy_connection_changed", "device", "connected", "name", "guid"), &Input::joy_connection_changed);  	ClassDB::bind_method(D_METHOD("is_joy_known", "device"), &Input::is_joy_known);  	ClassDB::bind_method(D_METHOD("get_joy_axis", "device", "axis"), &Input::get_joy_axis);  	ClassDB::bind_method(D_METHOD("get_joy_name", "device"), &Input::get_joy_name); @@ -121,6 +117,10 @@ void Input::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer);  	ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer);  	ClassDB::bind_method(D_METHOD("get_gyroscope"), &Input::get_gyroscope); +	ClassDB::bind_method(D_METHOD("set_gravity", "value"), &Input::set_gravity); +	ClassDB::bind_method(D_METHOD("set_accelerometer", "value"), &Input::set_accelerometer); +	ClassDB::bind_method(D_METHOD("set_magnetometer", "value"), &Input::set_magnetometer); +	ClassDB::bind_method(D_METHOD("set_gyroscope", "value"), &Input::set_gyroscope);  	ClassDB::bind_method(D_METHOD("get_last_mouse_speed"), &Input::get_last_mouse_speed);  	ClassDB::bind_method(D_METHOD("get_mouse_button_mask"), &Input::get_mouse_button_mask);  	ClassDB::bind_method(D_METHOD("set_mouse_mode", "mode"), &Input::set_mouse_mode); @@ -133,6 +133,7 @@ void Input::_bind_methods() {  	ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2()));  	ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event);  	ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input); +	ClassDB::bind_method(D_METHOD("flush_buffered_events"), &Input::flush_buffered_events);  	BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);  	BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN); @@ -162,14 +163,12 @@ void Input::_bind_methods() {  }  void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { -#ifdef TOOLS_ENABLED -	const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; -  	String pf = p_function; -	if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || -							  pf == "is_action_just_pressed" || pf == "is_action_just_released" || -							  pf == "get_action_strength" || pf == "get_action_raw_strength" || -							  pf == "get_axis" || pf == "get_vector")) { +	if (p_idx == 0 && +			(pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || +					pf == "is_action_just_pressed" || pf == "is_action_just_released" || +					pf == "get_action_strength" || pf == "get_action_raw_strength" || +					pf == "get_axis" || pf == "get_vector")) {  		List<PropertyInfo> pinfo;  		ProjectSettings::get_singleton()->get_property_list(&pinfo); @@ -179,10 +178,9 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S  			}  			String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); -			r_options->push_back(name.quote(quote_style)); +			r_options->push_back(name.quote());  		}  	} -#endif  }  void Input::SpeedTrack::update(const Vector2 &p_delta_p) { @@ -225,13 +223,22 @@ bool Input::is_key_pressed(Key p_keycode) const {  	return keys_pressed.has(p_keycode);  } +bool Input::is_physical_key_pressed(Key p_keycode) const { +	_THREAD_SAFE_METHOD_ +	return physical_keys_pressed.has(p_keycode); +} +  bool Input::is_mouse_button_pressed(MouseButton p_button) const {  	_THREAD_SAFE_METHOD_ -	return (mouse_button_mask & (1 << (p_button - 1))) != 0; +	return (mouse_button_mask & mouse_button_to_mask(p_button)) != MouseButton::NONE;  } -static int _combine_device(int p_value, int p_device) { -	return p_value | (p_device << 20); +static JoyAxis _combine_device(JoyAxis p_value, int p_device) { +	return JoyAxis((int)p_value | (p_device << 20)); +} + +static JoyButton _combine_device(JoyButton p_value, int p_device) { +	return JoyButton((int)p_value | (p_device << 20));  }  bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const { @@ -318,11 +325,11 @@ Vector2 Input::get_vector(const StringName &p_negative_x, const StringName &p_po  	if (p_deadzone < 0.0f) {  		// If the deadzone isn't specified, get it from the average of the actions. -		p_deadzone = (InputMap::get_singleton()->action_get_deadzone(p_positive_x) + -							 InputMap::get_singleton()->action_get_deadzone(p_negative_x) + -							 InputMap::get_singleton()->action_get_deadzone(p_positive_y) + -							 InputMap::get_singleton()->action_get_deadzone(p_negative_y)) / -					 4; +		p_deadzone = 0.25 * +				(InputMap::get_singleton()->action_get_deadzone(p_positive_x) + +						InputMap::get_singleton()->action_get_deadzone(p_negative_x) + +						InputMap::get_singleton()->action_get_deadzone(p_positive_y) + +						InputMap::get_singleton()->action_get_deadzone(p_negative_y));  	}  	// Circular length limiting and deadzone. @@ -340,7 +347,7 @@ Vector2 Input::get_vector(const StringName &p_negative_x, const StringName &p_po  float Input::get_joy_axis(int p_device, JoyAxis p_axis) const {  	_THREAD_SAFE_METHOD_ -	int c = _combine_device(p_axis, p_device); +	JoyAxis c = _combine_device(p_axis, p_device);  	if (_joy_axis.has(c)) {  		return _joy_axis[c];  	} else { @@ -396,7 +403,7 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, S  	if (p_connected) {  		String uidname = p_guid; -		if (p_guid == "") { +		if (p_guid.is_empty()) {  			int uidlen = MIN(p_name.length(), 16);  			for (int i = 0; i < uidlen; i++) {  				uidname = uidname + _hex_str(p_name[i]); @@ -414,11 +421,11 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, S  		js.mapping = mapping;  	} else {  		js.connected = false; -		for (int i = 0; i < JOY_BUTTON_MAX; i++) { -			int c = _combine_device(i, p_idx); +		for (int i = 0; i < (int)JoyButton::MAX; i++) { +			JoyButton c = _combine_device((JoyButton)i, p_idx);  			joy_buttons_pressed.erase(c);  		} -		for (int i = 0; i < JOY_AXIS_MAX; i++) { +		for (int i = 0; i < (int)JoyAxis::MAX; i++) {  			set_joy_axis(p_idx, (JoyAxis)i, 0.0f);  		}  	} @@ -456,21 +463,28 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em  	//   require additional handling by this class.  	Ref<InputEventKey> k = p_event; -	if (k.is_valid() && !k->is_echo() && k->get_keycode() != 0) { +	if (k.is_valid() && !k->is_echo() && k->get_keycode() != Key::NONE) {  		if (k->is_pressed()) {  			keys_pressed.insert(k->get_keycode());  		} else {  			keys_pressed.erase(k->get_keycode());  		}  	} +	if (k.is_valid() && !k->is_echo() && k->get_physical_keycode() != Key::NONE) { +		if (k->is_pressed()) { +			physical_keys_pressed.insert(k->get_physical_keycode()); +		} else { +			physical_keys_pressed.erase(k->get_physical_keycode()); +		} +	}  	Ref<InputEventMouseButton> mb = p_event;  	if (mb.is_valid()) {  		if (mb->is_pressed()) { -			mouse_button_mask |= (MouseButton)(1 << (mb->get_button_index() - 1)); +			mouse_button_mask |= mouse_button_to_mask(mb->get_button_index());  		} else { -			mouse_button_mask &= (MouseButton) ~(1 << (mb->get_button_index() - 1)); +			mouse_button_mask &= ~mouse_button_to_mask(mb->get_button_index());  		}  		Point2 pos = mb->get_global_position(); @@ -478,7 +492,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em  			set_mouse_position(pos);  		} -		if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && mb->get_button_index() == 1) { +		if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && mb->get_button_index() == MouseButton::LEFT) {  			Ref<InputEventScreenTouch> touch_event;  			touch_event.instantiate();  			touch_event->set_pressed(mb->is_pressed()); @@ -495,7 +509,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em  			set_mouse_position(pos);  		} -		if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && mm->get_button_mask() & 1) { +		if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && (mm->get_button_mask() & MouseButton::LEFT) != MouseButton::NONE) {  			Ref<InputEventScreenDrag> drag_event;  			drag_event.instantiate(); @@ -541,11 +555,11 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em  				button_event->set_position(st->get_position());  				button_event->set_global_position(st->get_position());  				button_event->set_pressed(st->is_pressed()); -				button_event->set_button_index(MOUSE_BUTTON_LEFT); +				button_event->set_button_index(MouseButton::LEFT);  				if (st->is_pressed()) { -					button_event->set_button_mask(MouseButton(mouse_button_mask | MOUSE_BUTTON_MASK_LEFT)); +					button_event->set_button_mask(MouseButton(mouse_button_mask | MouseButton::MASK_LEFT));  				} else { -					button_event->set_button_mask(MouseButton(mouse_button_mask & ~MOUSE_BUTTON_MASK_LEFT)); +					button_event->set_button_mask(MouseButton(mouse_button_mask & ~MouseButton::MASK_LEFT));  				}  				_parse_input_event_impl(button_event, true); @@ -578,7 +592,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em  	Ref<InputEventJoypadButton> jb = p_event;  	if (jb.is_valid()) { -		int c = _combine_device(jb->get_button_index(), jb->get_device()); +		JoyButton c = _combine_device(jb->get_button_index(), jb->get_device());  		if (jb->is_pressed()) {  			joy_buttons_pressed.insert(c); @@ -626,7 +640,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em  void Input::set_joy_axis(int p_device, JoyAxis p_axis, float p_value) {  	_THREAD_SAFE_METHOD_ -	int c = _combine_device(p_axis, p_device); +	JoyAxis c = _combine_device(p_axis, p_device);  	_joy_axis[c] = p_value;  } @@ -694,7 +708,7 @@ Point2 Input::get_last_mouse_speed() const {  	return mouse_speed_track.speed;  } -int Input::get_mouse_button_mask() const { +MouseButton Input::get_mouse_button_mask() const {  	return mouse_button_mask; // do not trust OS implementation, should remove it - OS::get_singleton()->get_mouse_button_state();  } @@ -712,11 +726,11 @@ Point2i Input::warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, con  	// detect the warp: if the relative distance is greater than the half of the size of the relevant rect  	// (checked per each axis), it will be considered as the consequence of a former pointer warp. -	const Point2i rel_sgn(p_motion->get_relative().x >= 0.0f ? 1 : -1, p_motion->get_relative().y >= 0.0 ? 1 : -1); +	const Point2i rel_sign(p_motion->get_relative().x >= 0.0f ? 1 : -1, p_motion->get_relative().y >= 0.0 ? 1 : -1);  	const Size2i warp_margin = p_rect.size * 0.5f;  	const Point2i rel_warped( -			Math::fmod(p_motion->get_relative().x + rel_sgn.x * warp_margin.x, p_rect.size.x) - rel_sgn.x * warp_margin.x, -			Math::fmod(p_motion->get_relative().y + rel_sgn.y * warp_margin.y, p_rect.size.y) - rel_sgn.y * warp_margin.y); +			Math::fmod(p_motion->get_relative().x + rel_sign.x * warp_margin.x, p_rect.size.x) - rel_sign.x * warp_margin.x, +			Math::fmod(p_motion->get_relative().y + rel_sign.y * warp_margin.y, p_rect.size.y) - rel_sign.y * warp_margin.y);  	const Point2i pos_local = p_motion->get_global_position() - p_rect.position;  	const Point2i pos_warped(Math::fposmod(pos_local.x, p_rect.size.x), Math::fposmod(pos_local.y, p_rect.size.y)); @@ -773,8 +787,8 @@ void Input::ensure_touch_mouse_raised() {  		button_event->set_position(mouse_pos);  		button_event->set_global_position(mouse_pos);  		button_event->set_pressed(false); -		button_event->set_button_index(MOUSE_BUTTON_LEFT); -		button_event->set_button_mask(MouseButton(mouse_button_mask & ~MOUSE_BUTTON_MASK_LEFT)); +		button_event->set_button_index(MouseButton::LEFT); +		button_event->set_button_mask(MouseButton(mouse_button_mask & ~MouseButton::MASK_LEFT));  		_parse_input_event_impl(button_event, true);  	} @@ -860,12 +874,13 @@ void Input::release_pressed_events() {  	flush_buffered_events(); // this is needed to release actions strengths  	keys_pressed.clear(); +	physical_keys_pressed.clear();  	joy_buttons_pressed.clear();  	_joy_axis.clear(); -	for (Map<StringName, Input::Action>::Element *E = action_state.front(); E; E = E->next()) { -		if (E->get().pressed) { -			action_release(E->key()); +	for (const KeyValue<StringName, Input::Action> &E : action_state) { +		if (E.value.pressed) { +			action_release(E.key);  		}  	}  } @@ -878,10 +893,10 @@ void Input::joy_button(int p_device, JoyButton p_button, bool p_pressed) {  	_THREAD_SAFE_METHOD_;  	Joypad &joy = joy_names[p_device];  	//printf("got button %i, mapping is %i\n", p_button, joy.mapping); -	if (joy.last_buttons[p_button] == p_pressed) { +	if (joy.last_buttons[(size_t)p_button] == p_pressed) {  		return;  	} -	joy.last_buttons[p_button] = p_pressed; +	joy.last_buttons[(size_t)p_button] = p_pressed;  	if (joy.mapping == -1) {  		_button_event(p_device, p_button, p_pressed);  		return; @@ -903,16 +918,16 @@ void Input::joy_button(int p_device, JoyButton p_button, bool p_pressed) {  void Input::joy_axis(int p_device, JoyAxis p_axis, const JoyAxisValue &p_value) {  	_THREAD_SAFE_METHOD_; -	ERR_FAIL_INDEX(p_axis, JOY_AXIS_MAX); +	ERR_FAIL_INDEX((int)p_axis, (int)JoyAxis::MAX);  	Joypad &joy = joy_names[p_device]; -	if (joy.last_axis[p_axis] == p_value.value) { +	if (joy.last_axis[(size_t)p_axis] == p_value.value) {  		return;  	}  	//when changing direction quickly, insert fake event to release pending inputmap actions -	float last = joy.last_axis[p_axis]; +	float last = joy.last_axis[(size_t)p_axis];  	if (p_value.min == 0 && (last < 0.25 || last > 0.75) && (last - 0.5) * (p_value.value - 0.5) < 0) {  		JoyAxisValue jx;  		jx.min = p_value.min; @@ -925,7 +940,7 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, const JoyAxisValue &p_value)  		joy_axis(p_device, p_axis, jx);  	} -	joy.last_axis[p_axis] = p_value.value; +	joy.last_axis[(size_t)p_axis] = p_value.value;  	float val = p_value.min == 0 ? -1.0f + 2.0f * p_value.value : p_value.value;  	if (joy.mapping == -1) { @@ -937,32 +952,32 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, const JoyAxisValue &p_value)  	if (map.type == TYPE_BUTTON) {  		bool pressed = map.value > 0.5; -		if (pressed == joy_buttons_pressed.has(_combine_device(map.index, p_device))) { +		if (pressed == joy_buttons_pressed.has(_combine_device((JoyButton)map.index, p_device))) {  			// Button already pressed or released; so ignore.  			return;  		}  		_button_event(p_device, (JoyButton)map.index, pressed);  		// Ensure opposite D-Pad button is also released. -		switch (map.index) { -			case JOY_BUTTON_DPAD_UP: -				if (joy_buttons_pressed.has(_combine_device(JOY_BUTTON_DPAD_DOWN, p_device))) { -					_button_event(p_device, JOY_BUTTON_DPAD_DOWN, false); +		switch ((JoyButton)map.index) { +			case JoyButton::DPAD_UP: +				if (joy_buttons_pressed.has(_combine_device(JoyButton::DPAD_DOWN, p_device))) { +					_button_event(p_device, JoyButton::DPAD_DOWN, false);  				}  				break; -			case JOY_BUTTON_DPAD_DOWN: -				if (joy_buttons_pressed.has(_combine_device(JOY_BUTTON_DPAD_UP, p_device))) { -					_button_event(p_device, JOY_BUTTON_DPAD_UP, false); +			case JoyButton::DPAD_DOWN: +				if (joy_buttons_pressed.has(_combine_device(JoyButton::DPAD_UP, p_device))) { +					_button_event(p_device, JoyButton::DPAD_UP, false);  				}  				break; -			case JOY_BUTTON_DPAD_LEFT: -				if (joy_buttons_pressed.has(_combine_device(JOY_BUTTON_DPAD_RIGHT, p_device))) { -					_button_event(p_device, JOY_BUTTON_DPAD_RIGHT, false); +			case JoyButton::DPAD_LEFT: +				if (joy_buttons_pressed.has(_combine_device(JoyButton::DPAD_RIGHT, p_device))) { +					_button_event(p_device, JoyButton::DPAD_RIGHT, false);  				}  				break; -			case JOY_BUTTON_DPAD_RIGHT: -				if (joy_buttons_pressed.has(_combine_device(JOY_BUTTON_DPAD_LEFT, p_device))) { -					_button_event(p_device, JOY_BUTTON_DPAD_LEFT, false); +			case JoyButton::DPAD_RIGHT: +				if (joy_buttons_pressed.has(_combine_device(JoyButton::DPAD_LEFT, p_device))) { +					_button_event(p_device, JoyButton::DPAD_LEFT, false);  				}  				break;  			default: @@ -979,27 +994,27 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, const JoyAxisValue &p_value)  	//printf("invalid mapping\n");  } -void Input::joy_hat(int p_device, int p_val) { +void Input::joy_hat(int p_device, HatMask p_val) {  	_THREAD_SAFE_METHOD_;  	const Joypad &joy = joy_names[p_device]; -	JoyEvent map[HAT_MAX]; +	JoyEvent map[(size_t)HatDir::MAX]; -	map[HatDir::HAT_UP].type = TYPE_BUTTON; -	map[HatDir::HAT_UP].index = JOY_BUTTON_DPAD_UP; -	map[HatDir::HAT_UP].value = 0; +	map[(size_t)HatDir::UP].type = TYPE_BUTTON; +	map[(size_t)HatDir::UP].index = (int)JoyButton::DPAD_UP; +	map[(size_t)HatDir::UP].value = 0; -	map[HatDir::HAT_RIGHT].type = TYPE_BUTTON; -	map[HatDir::HAT_RIGHT].index = JOY_BUTTON_DPAD_RIGHT; -	map[HatDir::HAT_RIGHT].value = 0; +	map[(size_t)HatDir::RIGHT].type = TYPE_BUTTON; +	map[(size_t)HatDir::RIGHT].index = (int)JoyButton::DPAD_RIGHT; +	map[(size_t)HatDir::RIGHT].value = 0; -	map[HatDir::HAT_DOWN].type = TYPE_BUTTON; -	map[HatDir::HAT_DOWN].index = JOY_BUTTON_DPAD_DOWN; -	map[HatDir::HAT_DOWN].value = 0; +	map[(size_t)HatDir::DOWN].type = TYPE_BUTTON; +	map[(size_t)HatDir::DOWN].index = (int)JoyButton::DPAD_DOWN; +	map[(size_t)HatDir::DOWN].value = 0; -	map[HatDir::HAT_LEFT].type = TYPE_BUTTON; -	map[HatDir::HAT_LEFT].index = JOY_BUTTON_DPAD_LEFT; -	map[HatDir::HAT_LEFT].value = 0; +	map[(size_t)HatDir::LEFT].type = TYPE_BUTTON; +	map[(size_t)HatDir::LEFT].index = (int)JoyButton::DPAD_LEFT; +	map[(size_t)HatDir::LEFT].value = 0;  	if (joy.mapping != -1) {  		_get_mapped_hat_events(map_db[joy.mapping], (HatDir)0, map); @@ -1007,18 +1022,18 @@ void Input::joy_hat(int p_device, int p_val) {  	int cur_val = joy_names[p_device].hat_current; -	for (int hat_direction = 0, hat_mask = 1; hat_direction < HAT_MAX; hat_direction++, hat_mask <<= 1) { -		if ((p_val & hat_mask) != (cur_val & hat_mask)) { +	for (int hat_direction = 0, hat_mask = 1; hat_direction < (int)HatDir::MAX; hat_direction++, hat_mask <<= 1) { +		if (((int)p_val & hat_mask) != (cur_val & hat_mask)) {  			if (map[hat_direction].type == TYPE_BUTTON) { -				_button_event(p_device, (JoyButton)map[hat_direction].index, p_val & hat_mask); +				_button_event(p_device, (JoyButton)map[hat_direction].index, (int)p_val & hat_mask);  			}  			if (map[hat_direction].type == TYPE_AXIS) { -				_axis_event(p_device, (JoyAxis)map[hat_direction].index, (p_val & hat_mask) ? map[hat_direction].value : 0.0); +				_axis_event(p_device, (JoyAxis)map[hat_direction].index, ((int)p_val & hat_mask) ? map[hat_direction].value : 0.0);  			}  		}  	} -	joy_names[p_device].hat_current = p_val; +	joy_names[p_device].hat_current = (int)p_val;  }  void Input::_button_event(int p_device, JoyButton p_index, bool p_pressed) { @@ -1051,10 +1066,10 @@ Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping,  			event.type = binding.outputType;  			switch (binding.outputType) {  				case TYPE_BUTTON: -					event.index = binding.output.button; +					event.index = (int)binding.output.button;  					return event;  				case TYPE_AXIS: -					event.index = binding.output.axis.axis; +					event.index = (int)binding.output.axis.axis;  					switch (binding.output.axis.range) {  						case POSITIVE_HALF_AXIS:  							event.value = 1; @@ -1106,7 +1121,7 @@ Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, J  				}  				switch (binding.outputType) {  					case TYPE_BUTTON: -						event.index = binding.output.button; +						event.index = (int)binding.output.button;  						switch (binding.input.axis.range) {  							case POSITIVE_HALF_AXIS:  								event.value = shifted_positive_value; @@ -1123,7 +1138,7 @@ Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, J  						}  						return event;  					case TYPE_AXIS: -						event.index = binding.output.axis.axis; +						event.index = (int)binding.output.axis.axis;  						event.value = value;  						if (binding.output.axis.range != binding.input.axis.range) {  							switch (binding.output.axis.range) { @@ -1152,43 +1167,43 @@ void Input::_get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat  	for (int i = 0; i < mapping.bindings.size(); i++) {  		const JoyBinding binding = mapping.bindings[i];  		if (binding.inputType == TYPE_HAT && binding.input.hat.hat == p_hat) { -			int hat_direction; +			HatDir hat_direction;  			switch (binding.input.hat.hat_mask) { -				case HatMask::HAT_MASK_UP: -					hat_direction = HatDir::HAT_UP; +				case HatMask::UP: +					hat_direction = HatDir::UP;  					break; -				case HatMask::HAT_MASK_RIGHT: -					hat_direction = HatDir::HAT_RIGHT; +				case HatMask::RIGHT: +					hat_direction = HatDir::RIGHT;  					break; -				case HatMask::HAT_MASK_DOWN: -					hat_direction = HatDir::HAT_DOWN; +				case HatMask::DOWN: +					hat_direction = HatDir::DOWN;  					break; -				case HatMask::HAT_MASK_LEFT: -					hat_direction = HatDir::HAT_LEFT; +				case HatMask::LEFT: +					hat_direction = HatDir::LEFT;  					break;  				default:  					ERR_PRINT_ONCE("Joypad button mapping error.");  					continue;  			} -			r_events[hat_direction].type = binding.outputType; +			r_events[(size_t)hat_direction].type = binding.outputType;  			switch (binding.outputType) {  				case TYPE_BUTTON: -					r_events[hat_direction].index = binding.output.button; +					r_events[(size_t)hat_direction].index = (int)binding.output.button;  					break;  				case TYPE_AXIS: -					r_events[hat_direction].index = binding.output.axis.axis; +					r_events[(size_t)hat_direction].index = (int)binding.output.axis.axis;  					switch (binding.output.axis.range) {  						case POSITIVE_HALF_AXIS: -							r_events[hat_direction].value = 1; +							r_events[(size_t)hat_direction].value = 1;  							break;  						case NEGATIVE_HALF_AXIS: -							r_events[hat_direction].value = -1; +							r_events[(size_t)hat_direction].value = -1;  							break;  						case FULL_AXIS:  							// It doesn't make sense for a hat direction to map to a full axis,  							// but keeping as a default for a trigger with a positive half-axis. -							r_events[hat_direction].value = 1; +							r_events[(size_t)hat_direction].value = 1;  							break;  					}  					break; @@ -1200,21 +1215,21 @@ void Input::_get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat  }  JoyButton Input::_get_output_button(String output) { -	for (int i = 0; i < JOY_BUTTON_SDL_MAX; i++) { +	for (int i = 0; i < (int)JoyButton::SDL_MAX; i++) {  		if (output == _joy_buttons[i]) {  			return JoyButton(i);  		}  	} -	return JoyButton::JOY_BUTTON_INVALID; +	return JoyButton::INVALID;  }  JoyAxis Input::_get_output_axis(String output) { -	for (int i = 0; i < JOY_AXIS_SDL_MAX; i++) { +	for (int i = 0; i < (int)JoyAxis::SDL_MAX; i++) {  		if (output == _joy_axes[i]) {  			return JoyAxis(i);  		}  	} -	return JoyAxis::JOY_AXIS_INVALID; +	return JoyAxis::INVALID;  }  void Input::parse_mapping(String p_mapping) { @@ -1234,14 +1249,14 @@ void Input::parse_mapping(String p_mapping) {  	int idx = 1;  	while (++idx < entry.size()) { -		if (entry[idx] == "") { +		if (entry[idx].is_empty()) {  			continue;  		}  		String output = entry[idx].get_slice(":", 0).replace(" ", "");  		String input = entry[idx].get_slice(":", 1).replace(" ", "");  		ERR_CONTINUE_MSG(output.length() < 1 || input.length() < 2, -				String(entry[idx] + "\nInvalid device mapping entry: " + entry[idx])); +				vformat("Invalid device mapping entry \"%s\" in mapping:\n%s", entry[idx], p_mapping));  		if (output == "platform" || output == "hint") {  			continue; @@ -1249,7 +1264,8 @@ void Input::parse_mapping(String p_mapping) {  		JoyAxisRange output_range = FULL_AXIS;  		if (output[0] == '+' || output[0] == '-') { -			ERR_CONTINUE_MSG(output.length() < 2, String(entry[idx] + "\nInvalid output: " + entry[idx])); +			ERR_CONTINUE_MSG(output.length() < 2, +					vformat("Invalid output entry \"%s\" in mapping:\n%s", entry[idx], p_mapping));  			if (output[0] == '+') {  				output_range = POSITIVE_HALF_AXIS;  			} else if (output[0] == '-') { @@ -1274,16 +1290,16 @@ void Input::parse_mapping(String p_mapping) {  		JoyButton output_button = _get_output_button(output);  		JoyAxis output_axis = _get_output_axis(output); -		ERR_CONTINUE_MSG(output_button == JOY_BUTTON_INVALID && output_axis == JOY_AXIS_INVALID, -				String(entry[idx] + "\nUnrecognised output string: " + output)); -		ERR_CONTINUE_MSG(output_button != JOY_BUTTON_INVALID && output_axis != JOY_AXIS_INVALID, -				String("BUG: Output string matched both button and axis: " + output)); +		ERR_CONTINUE_MSG(output_button == JoyButton::INVALID && output_axis == JoyAxis::INVALID, +				vformat("Unrecognised output string \"%s\" in mapping:\n%s", output, p_mapping)); +		ERR_CONTINUE_MSG(output_button != JoyButton::INVALID && output_axis != JoyAxis::INVALID, +				vformat("Output string \"%s\" matched both button and axis in mapping:\n%s", output, p_mapping));  		JoyBinding binding; -		if (output_button != JOY_BUTTON_INVALID) { +		if (output_button != JoyButton::INVALID) {  			binding.outputType = TYPE_BUTTON;  			binding.output.button = output_button; -		} else if (output_axis != JOY_AXIS_INVALID) { +		} else if (output_axis != JoyAxis::INVALID) {  			binding.outputType = TYPE_AXIS;  			binding.output.axis.axis = output_axis;  			binding.output.axis.range = output_range; @@ -1302,13 +1318,13 @@ void Input::parse_mapping(String p_mapping) {  				break;  			case 'h':  				ERR_CONTINUE_MSG(input.length() != 4 || input[2] != '.', -						String(entry[idx] + "\nInvalid hat input: " + input)); +						vformat("Invalid had input \"%s\" in mapping:\n%s", input, p_mapping));  				binding.inputType = TYPE_HAT;  				binding.input.hat.hat = (HatDir)input.substr(1, 1).to_int();  				binding.input.hat.hat_mask = static_cast<HatMask>(input.substr(3).to_int());  				break;  			default: -				ERR_CONTINUE_MSG(true, String(entry[idx] + "\nUnrecognised input string: " + input)); +				ERR_CONTINUE_MSG(true, vformat("Unrecognized input string \"%s\" in mapping:\n%s", input, p_mapping));  		}  		mapping.bindings.push_back(binding); @@ -1322,8 +1338,8 @@ void Input::add_joy_mapping(String p_mapping, bool p_update_existing) {  	if (p_update_existing) {  		Vector<String> entry = p_mapping.split(",");  		String uid = entry[0]; -		for (Map<int, Joypad>::Element *E = joy_names.front(); E; E = E->next()) { -			Joypad &joy = E->get(); +		for (KeyValue<int, Joypad> &E : joy_names) { +			Joypad &joy = E.value;  			if (joy.uid == uid) {  				joy.mapping = map_db.size() - 1;  			} @@ -1334,11 +1350,11 @@ void Input::add_joy_mapping(String p_mapping, bool p_update_existing) {  void Input::remove_joy_mapping(String p_guid) {  	for (int i = map_db.size() - 1; i >= 0; i--) {  		if (p_guid == map_db[i].uid) { -			map_db.remove(i); +			map_db.remove_at(i);  		}  	} -	for (Map<int, Joypad>::Element *E = joy_names.front(); E; E = E->next()) { -		Joypad &joy = E->get(); +	for (KeyValue<int, Joypad> &E : joy_names) { +		Joypad &joy = E.value;  		if (joy.uid == p_guid) {  			joy.mapping = -1;  		} @@ -1404,10 +1420,10 @@ Input::Input() {  	// If defined, parse SDL_GAMECONTROLLERCONFIG for possible new mappings/overrides.  	String env_mapping = OS::get_singleton()->get_environment("SDL_GAMECONTROLLERCONFIG"); -	if (env_mapping != "") { +	if (!env_mapping.is_empty()) {  		Vector<String> entries = env_mapping.split("\n");  		for (int i = 0; i < entries.size(); i++) { -			if (entries[i] == "") { +			if (entries[i].is_empty()) {  				continue;  			}  			parse_mapping(entries[i]); diff --git a/core/input/input.h b/core/input/input.h index 6819fc8eb0..faec654a3c 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -85,11 +85,12 @@ public:  	typedef void (*EventDispatchFunc)(const Ref<InputEvent> &p_event);  private: -	int mouse_button_mask = 0; +	MouseButton mouse_button_mask = MouseButton::NONE; -	Set<int> keys_pressed; -	Set<int> joy_buttons_pressed; -	Map<int, float> _joy_axis; +	Set<Key> physical_keys_pressed; +	Set<Key> keys_pressed; +	Set<JoyButton> joy_buttons_pressed; +	Map<JoyAxis, float> _joy_axis;  	//Map<StringName,int> custom_action_press;  	Vector3 gravity;  	Vector3 accelerometer; @@ -133,9 +134,9 @@ private:  		StringName name;  		StringName uid;  		bool connected = false; -		bool last_buttons[JOY_BUTTON_MAX] = { false }; -		float last_axis[JOY_AXIS_MAX] = { 0.0f }; -		int last_hat = HatMask::HAT_MASK_CENTER; +		bool last_buttons[(size_t)JoyButton::MAX] = { false }; +		float last_axis[(size_t)JoyAxis::MAX] = { 0.0f }; +		HatMask last_hat = HatMask::CENTER;  		int mapping = -1;  		int hat_current = 0;  	}; @@ -162,7 +163,7 @@ private:  	struct JoyEvent {  		int type; -		int index; +		int index; // Can be either JoyAxis or JoyButton.  		float value;  	}; @@ -206,7 +207,7 @@ private:  	JoyEvent _get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button);  	JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value); -	void _get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat, JoyEvent r_events[HAT_MAX]); +	void _get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat, JoyEvent r_events[(size_t)HatDir::MAX]);  	JoyButton _get_output_button(String output);  	JoyAxis _get_output_axis(String output);  	void _button_event(int p_device, JoyButton p_index, bool p_pressed); @@ -247,6 +248,7 @@ public:  	static Input *get_singleton();  	bool is_key_pressed(Key p_keycode) const; +	bool is_physical_key_pressed(Key p_keycode) const;  	bool is_mouse_button_pressed(MouseButton p_button) const;  	bool is_joy_button_pressed(int p_device, JoyButton p_button) const;  	bool is_action_pressed(const StringName &p_action, bool p_exact = false) const; @@ -265,7 +267,6 @@ public:  	float get_joy_vibration_duration(int p_device);  	uint64_t get_joy_vibration_timestamp(int p_device);  	void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = ""); -	void parse_joypad_mapping(String p_mapping, bool p_update_existing);  	Vector3 get_gravity() const;  	Vector3 get_accelerometer() const; @@ -274,7 +275,7 @@ public:  	Point2 get_mouse_position() const;  	Point2 get_last_mouse_speed() const; -	int get_mouse_button_mask() const; +	MouseButton get_mouse_button_mask() const;  	void warp_mouse_position(const Vector2 &p_to);  	Point2i warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, const Rect2 &p_rect); @@ -313,7 +314,7 @@ public:  	void parse_mapping(String p_mapping);  	void joy_button(int p_device, JoyButton p_button, bool p_pressed);  	void joy_axis(int p_device, JoyAxis p_axis, const JoyAxisValue &p_value); -	void joy_hat(int p_device, int p_val); +	void joy_hat(int p_device, HatMask p_val);  	void add_joy_mapping(String p_mapping, bool p_update_existing = false);  	void remove_joy_mapping(String p_guid); diff --git a/core/input/input_enums.h b/core/input/input_enums.h index 4479a85bfe..8601007ac9 100644 --- a/core/input/input_enums.h +++ b/core/input/input_enums.h @@ -31,90 +31,106 @@  #ifndef INPUT_ENUMS_H  #define INPUT_ENUMS_H -enum HatDir { -	HAT_UP = 0, -	HAT_RIGHT = 1, -	HAT_DOWN = 2, -	HAT_LEFT = 3, -	HAT_MAX = 4, +enum class HatDir { +	UP = 0, +	RIGHT = 1, +	DOWN = 2, +	LEFT = 3, +	MAX = 4,  }; -enum HatMask { -	HAT_MASK_CENTER = 0, -	HAT_MASK_UP = 1, -	HAT_MASK_RIGHT = 2, -	HAT_MASK_DOWN = 4, -	HAT_MASK_LEFT = 8, +enum class HatMask { +	CENTER = 0, +	UP = 1, +	RIGHT = 2, +	DOWN = 4, +	LEFT = 8,  }; -enum JoyAxis { -	JOY_AXIS_INVALID = -1, -	JOY_AXIS_LEFT_X = 0, -	JOY_AXIS_LEFT_Y = 1, -	JOY_AXIS_RIGHT_X = 2, -	JOY_AXIS_RIGHT_Y = 3, -	JOY_AXIS_TRIGGER_LEFT = 4, -	JOY_AXIS_TRIGGER_RIGHT = 5, -	JOY_AXIS_SDL_MAX = 6, -	JOY_AXIS_MAX = 10, // OpenVR supports up to 5 Joysticks making a total of 10 axes. +enum class JoyAxis { +	INVALID = -1, +	LEFT_X = 0, +	LEFT_Y = 1, +	RIGHT_X = 2, +	RIGHT_Y = 3, +	TRIGGER_LEFT = 4, +	TRIGGER_RIGHT = 5, +	SDL_MAX = 6, +	MAX = 10, // OpenVR supports up to 5 Joysticks making a total of 10 axes.  }; -enum JoyButton { -	JOY_BUTTON_INVALID = -1, -	JOY_BUTTON_A = 0, -	JOY_BUTTON_B = 1, -	JOY_BUTTON_X = 2, -	JOY_BUTTON_Y = 3, -	JOY_BUTTON_BACK = 4, -	JOY_BUTTON_GUIDE = 5, -	JOY_BUTTON_START = 6, -	JOY_BUTTON_LEFT_STICK = 7, -	JOY_BUTTON_RIGHT_STICK = 8, -	JOY_BUTTON_LEFT_SHOULDER = 9, -	JOY_BUTTON_RIGHT_SHOULDER = 10, -	JOY_BUTTON_DPAD_UP = 11, -	JOY_BUTTON_DPAD_DOWN = 12, -	JOY_BUTTON_DPAD_LEFT = 13, -	JOY_BUTTON_DPAD_RIGHT = 14, -	JOY_BUTTON_MISC1 = 15, -	JOY_BUTTON_PADDLE1 = 16, -	JOY_BUTTON_PADDLE2 = 17, -	JOY_BUTTON_PADDLE3 = 18, -	JOY_BUTTON_PADDLE4 = 19, -	JOY_BUTTON_TOUCHPAD = 20, -	JOY_BUTTON_SDL_MAX = 21, -	JOY_BUTTON_MAX = 36, // Android supports up to 36 buttons. +enum class JoyButton { +	INVALID = -1, +	A = 0, +	B = 1, +	X = 2, +	Y = 3, +	BACK = 4, +	GUIDE = 5, +	START = 6, +	LEFT_STICK = 7, +	RIGHT_STICK = 8, +	LEFT_SHOULDER = 9, +	RIGHT_SHOULDER = 10, +	DPAD_UP = 11, +	DPAD_DOWN = 12, +	DPAD_LEFT = 13, +	DPAD_RIGHT = 14, +	MISC1 = 15, +	PADDLE1 = 16, +	PADDLE2 = 17, +	PADDLE3 = 18, +	PADDLE4 = 19, +	TOUCHPAD = 20, +	SDL_MAX = 21, +	MAX = 36, // Android supports up to 36 buttons.  }; -enum MIDIMessage { -	MIDI_MESSAGE_NONE = 0, -	MIDI_MESSAGE_NOTE_OFF = 0x8, -	MIDI_MESSAGE_NOTE_ON = 0x9, -	MIDI_MESSAGE_AFTERTOUCH = 0xA, -	MIDI_MESSAGE_CONTROL_CHANGE = 0xB, -	MIDI_MESSAGE_PROGRAM_CHANGE = 0xC, -	MIDI_MESSAGE_CHANNEL_PRESSURE = 0xD, -	MIDI_MESSAGE_PITCH_BEND = 0xE, +enum class MIDIMessage { +	NONE = 0, +	NOTE_OFF = 0x8, +	NOTE_ON = 0x9, +	AFTERTOUCH = 0xA, +	CONTROL_CHANGE = 0xB, +	PROGRAM_CHANGE = 0xC, +	CHANNEL_PRESSURE = 0xD, +	PITCH_BEND = 0xE,  }; -enum MouseButton { -	MOUSE_BUTTON_NONE = 0, -	MOUSE_BUTTON_LEFT = 1, -	MOUSE_BUTTON_RIGHT = 2, -	MOUSE_BUTTON_MIDDLE = 3, -	MOUSE_BUTTON_WHEEL_UP = 4, -	MOUSE_BUTTON_WHEEL_DOWN = 5, -	MOUSE_BUTTON_WHEEL_LEFT = 6, -	MOUSE_BUTTON_WHEEL_RIGHT = 7, -	MOUSE_BUTTON_XBUTTON1 = 8, -	MOUSE_BUTTON_XBUTTON2 = 9, -	MOUSE_BUTTON_MASK_LEFT = (1 << (MOUSE_BUTTON_LEFT - 1)), -	MOUSE_BUTTON_MASK_RIGHT = (1 << (MOUSE_BUTTON_RIGHT - 1)), -	MOUSE_BUTTON_MASK_MIDDLE = (1 << (MOUSE_BUTTON_MIDDLE - 1)), -	MOUSE_BUTTON_MASK_XBUTTON1 = (1 << (MOUSE_BUTTON_XBUTTON1 - 1)), -	MOUSE_BUTTON_MASK_XBUTTON2 = (1 << (MOUSE_BUTTON_XBUTTON2 - 1)), +enum class MouseButton { +	NONE = 0, +	LEFT = 1, +	RIGHT = 2, +	MIDDLE = 3, +	WHEEL_UP = 4, +	WHEEL_DOWN = 5, +	WHEEL_LEFT = 6, +	WHEEL_RIGHT = 7, +	MB_XBUTTON1 = 8, // "XBUTTON1" is a reserved word on Windows. +	MB_XBUTTON2 = 9, // "XBUTTON2" is a reserved word on Windows. +	MASK_LEFT = (1 << (LEFT - 1)), +	MASK_RIGHT = (1 << (RIGHT - 1)), +	MASK_MIDDLE = (1 << (MIDDLE - 1)), +	MASK_XBUTTON1 = (1 << (MB_XBUTTON1 - 1)), +	MASK_XBUTTON2 = (1 << (MB_XBUTTON2 - 1)),  }; +inline MouseButton mouse_button_to_mask(MouseButton button) { +	return MouseButton(1 << ((int)button - 1)); +} + +inline MouseButton operator&(MouseButton a, MouseButton b) { +	return (MouseButton)((int)a & (int)b); +} + +inline MouseButton operator|(MouseButton a, MouseButton b) { +	return (MouseButton)((int)a | (int)b); +} + +inline MouseButton operator^(MouseButton a, MouseButton b) { +	return (MouseButton)((int)a ^ (int)b); +} +  inline MouseButton &operator|=(MouseButton &a, MouseButton b) {  	return (MouseButton &)((int &)a |= (int)b);  } @@ -123,4 +139,28 @@ inline MouseButton &operator&=(MouseButton &a, MouseButton b) {  	return (MouseButton &)((int &)a &= (int)b);  } +inline MouseButton operator~(MouseButton a) { +	return (MouseButton)(~(int)a); +} + +inline HatMask operator|(HatMask a, HatMask b) { +	return (HatMask)((int)a | (int)b); +} + +inline HatMask operator&(HatMask a, HatMask b) { +	return (HatMask)((int)a & (int)b); +} + +inline HatMask &operator&=(HatMask &a, HatMask b) { +	return (HatMask &)((int &)a &= (int)b); +} + +inline HatMask &operator|=(HatMask &a, HatMask b) { +	return (HatMask &)((int &)a |= (int)b); +} + +inline HatMask operator~(HatMask a) { +	return (HatMask)(~(int)a); +} +  #endif // INPUT_ENUMS_H diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index 50b2099236..7c98fa9540 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -203,19 +203,19 @@ void InputEventWithModifiers::set_modifiers_from_event(const InputEventWithModif  	set_meta_pressed(event->is_meta_pressed());  } -uint32_t InputEventWithModifiers::get_modifiers_mask() const { -	uint32_t mask = 0; +Key InputEventWithModifiers::get_modifiers_mask() const { +	Key mask = Key::NONE;  	if (is_ctrl_pressed()) { -		mask |= KEY_MASK_CTRL; +		mask |= KeyModifierMask::CTRL;  	}  	if (is_shift_pressed()) { -		mask |= KEY_MASK_SHIFT; +		mask |= KeyModifierMask::SHIFT;  	}  	if (is_alt_pressed()) { -		mask |= KEY_MASK_ALT; +		mask |= KeyModifierMask::ALT;  	}  	if (is_meta_pressed()) { -		mask |= KEY_MASK_META; +		mask |= KeyModifierMask::META;  	}  	return mask;  } @@ -224,16 +224,16 @@ String InputEventWithModifiers::as_text() const {  	Vector<String> mod_names;  	if (is_ctrl_pressed()) { -		mod_names.push_back(find_keycode_name(KEY_CTRL)); +		mod_names.push_back(find_keycode_name(Key::CTRL));  	}  	if (is_shift_pressed()) { -		mod_names.push_back(find_keycode_name(KEY_SHIFT)); +		mod_names.push_back(find_keycode_name(Key::SHIFT));  	}  	if (is_alt_pressed()) { -		mod_names.push_back(find_keycode_name(KEY_ALT)); +		mod_names.push_back(find_keycode_name(Key::ALT));  	}  	if (is_meta_pressed()) { -		mod_names.push_back(find_keycode_name(KEY_META)); +		mod_names.push_back(find_keycode_name(Key::META));  	}  	if (!mod_names.is_empty()) { @@ -325,12 +325,12 @@ Key InputEventKey::get_physical_keycode() const {  	return physical_keycode;  } -void InputEventKey::set_unicode(uint32_t p_unicode) { +void InputEventKey::set_unicode(char32_t p_unicode) {  	unicode = p_unicode;  	emit_changed();  } -uint32_t InputEventKey::get_unicode() const { +char32_t InputEventKey::get_unicode() const {  	return unicode;  } @@ -343,29 +343,29 @@ bool InputEventKey::is_echo() const {  	return echo;  } -uint32_t InputEventKey::get_keycode_with_modifiers() const { +Key InputEventKey::get_keycode_with_modifiers() const {  	return keycode | get_modifiers_mask();  } -uint32_t InputEventKey::get_physical_keycode_with_modifiers() const { +Key InputEventKey::get_physical_keycode_with_modifiers() const {  	return physical_keycode | get_modifiers_mask();  }  String InputEventKey::as_text() const {  	String kc; -	if (keycode == 0) { +	if (keycode == Key::NONE) {  		kc = keycode_get_string(physical_keycode) + " (" + RTR("Physical") + ")";  	} else {  		kc = keycode_get_string(keycode);  	} -	if (kc == String()) { +	if (kc.is_empty()) {  		return kc;  	}  	String mods_text = InputEventWithModifiers::as_text(); -	return mods_text == "" ? kc : mods_text + "+" + kc; +	return mods_text.is_empty() ? kc : mods_text + "+" + kc;  }  String InputEventKey::to_string() { @@ -374,15 +374,15 @@ String InputEventKey::to_string() {  	String kc = "";  	String physical = "false"; -	if (keycode == 0) { -		kc = itos(physical_keycode) + " (" + keycode_get_string(physical_keycode) + ")"; +	if (keycode == Key::NONE) { +		kc = itos((int64_t)physical_keycode) + " (" + keycode_get_string(physical_keycode) + ")";  		physical = "true";  	} else { -		kc = itos(keycode) + " (" + keycode_get_string(keycode) + ")"; +		kc = itos((int64_t)keycode) + " (" + keycode_get_string(keycode) + ")";  	}  	String mods = InputEventWithModifiers::as_text(); -	mods = mods == "" ? TTR("none") : mods; +	mods = mods.is_empty() ? TTR("none") : mods;  	return vformat("InputEventKey: keycode=%s, mods=%s, physical=%s, pressed=%s, echo=%s", kc, mods, physical, p, e);  } @@ -390,22 +390,22 @@ String InputEventKey::to_string() {  Ref<InputEventKey> InputEventKey::create_reference(Key p_keycode) {  	Ref<InputEventKey> ie;  	ie.instantiate(); -	ie->set_keycode(p_keycode & KEY_CODE_MASK); -	ie->set_unicode(p_keycode & KEY_CODE_MASK); +	ie->set_keycode(p_keycode & KeyModifierMask::CODE_MASK); +	ie->set_unicode(char32_t(p_keycode & KeyModifierMask::CODE_MASK)); -	if (p_keycode & KEY_MASK_SHIFT) { +	if ((p_keycode & KeyModifierMask::SHIFT) != Key::NONE) {  		ie->set_shift_pressed(true);  	} -	if (p_keycode & KEY_MASK_ALT) { +	if ((p_keycode & KeyModifierMask::ALT) != Key::NONE) {  		ie->set_alt_pressed(true);  	} -	if (p_keycode & KEY_MASK_CTRL) { +	if ((p_keycode & KeyModifierMask::CTRL) != Key::NONE) {  		ie->set_ctrl_pressed(true);  	} -	if (p_keycode & KEY_MASK_CMD) { +	if ((p_keycode & KeyModifierMask::CMD) != Key::NONE) {  		ie->set_command_pressed(true);  	} -	if (p_keycode & KEY_MASK_META) { +	if ((p_keycode & KeyModifierMask::META) != Key::NONE) {  		ie->set_meta_pressed(true);  	} @@ -419,14 +419,14 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed  	}  	bool match = false; -	if (get_keycode() == 0) { -		uint32_t code = get_physical_keycode_with_modifiers(); -		uint32_t event_code = key->get_physical_keycode_with_modifiers(); +	if (get_keycode() == Key::NONE) { +		Key code = get_physical_keycode_with_modifiers(); +		Key event_code = key->get_physical_keycode_with_modifiers();  		match = get_physical_keycode() == key->get_physical_keycode() && (!key->is_pressed() || (code & event_code) == code);  	} else { -		uint32_t code = get_keycode_with_modifiers(); -		uint32_t event_code = key->get_keycode_with_modifiers(); +		Key code = get_keycode_with_modifiers(); +		Key event_code = key->get_keycode_with_modifiers();  		match = get_keycode() == key->get_keycode() && (!key->is_pressed() || (code & event_code) == code);  	} @@ -452,8 +452,13 @@ bool InputEventKey::is_match(const Ref<InputEvent> &p_event, bool p_exact_match)  		return false;  	} -	return keycode == key->keycode && -		   (!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask()); +	if (keycode == Key::NONE) { +		return physical_keycode == key->physical_keycode && +				(!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask()); +	} else { +		return keycode == key->keycode && +				(!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask()); +	}  }  void InputEventKey::_bind_methods() { @@ -482,12 +487,12 @@ void InputEventKey::_bind_methods() {  /////////////////////////////////// -void InputEventMouse::set_button_mask(int p_mask) { +void InputEventMouse::set_button_mask(MouseButton p_mask) {  	button_mask = p_mask;  	emit_changed();  } -int InputEventMouse::get_button_mask() const { +MouseButton InputEventMouse::get_button_mask() const {  	return button_mask;  } @@ -611,7 +616,7 @@ bool InputEventMouseButton::is_match(const Ref<InputEvent> &p_event, bool p_exac  	}  	return button_index == mb->button_index && -		   (!p_exact_match || get_modifiers_mask() == mb->get_modifiers_mask()); +			(!p_exact_match || get_modifiers_mask() == mb->get_modifiers_mask());  }  static const char *_mouse_button_descriptions[9] = { @@ -629,24 +634,24 @@ static const char *_mouse_button_descriptions[9] = {  String InputEventMouseButton::as_text() const {  	// Modifiers  	String mods_text = InputEventWithModifiers::as_text(); -	String full_string = mods_text == "" ? "" : mods_text + "+"; +	String full_string = mods_text.is_empty() ? "" : mods_text + "+";  	// Button -	int idx = get_button_index(); +	MouseButton idx = get_button_index();  	switch (idx) { -		case MOUSE_BUTTON_LEFT: -		case MOUSE_BUTTON_RIGHT: -		case MOUSE_BUTTON_MIDDLE: -		case MOUSE_BUTTON_WHEEL_UP: -		case MOUSE_BUTTON_WHEEL_DOWN: -		case MOUSE_BUTTON_WHEEL_LEFT: -		case MOUSE_BUTTON_WHEEL_RIGHT: -		case MOUSE_BUTTON_XBUTTON1: -		case MOUSE_BUTTON_XBUTTON2: -			full_string += RTR(_mouse_button_descriptions[idx - 1]); // button index starts from 1, array index starts from 0, so subtract 1 +		case MouseButton::LEFT: +		case MouseButton::RIGHT: +		case MouseButton::MIDDLE: +		case MouseButton::WHEEL_UP: +		case MouseButton::WHEEL_DOWN: +		case MouseButton::WHEEL_LEFT: +		case MouseButton::WHEEL_RIGHT: +		case MouseButton::MB_XBUTTON1: +		case MouseButton::MB_XBUTTON2: +			full_string += RTR(_mouse_button_descriptions[(size_t)idx - 1]); // button index starts from 1, array index starts from 0, so subtract 1  			break;  		default: -			full_string += RTR("Button") + " #" + itos(idx); +			full_string += RTR("Button") + " #" + itos((int64_t)idx);  			break;  	} @@ -662,27 +667,27 @@ String InputEventMouseButton::to_string() {  	String p = is_pressed() ? "true" : "false";  	String d = double_click ? "true" : "false"; -	int idx = get_button_index(); -	String button_string = itos(idx); +	MouseButton idx = get_button_index(); +	String button_string = itos((int64_t)idx);  	switch (idx) { -		case MOUSE_BUTTON_LEFT: -		case MOUSE_BUTTON_RIGHT: -		case MOUSE_BUTTON_MIDDLE: -		case MOUSE_BUTTON_WHEEL_UP: -		case MOUSE_BUTTON_WHEEL_DOWN: -		case MOUSE_BUTTON_WHEEL_LEFT: -		case MOUSE_BUTTON_WHEEL_RIGHT: -		case MOUSE_BUTTON_XBUTTON1: -		case MOUSE_BUTTON_XBUTTON2: -			button_string += " (" + RTR(_mouse_button_descriptions[idx - 1]) + ")"; // button index starts from 1, array index starts from 0, so subtract 1 +		case MouseButton::LEFT: +		case MouseButton::RIGHT: +		case MouseButton::MIDDLE: +		case MouseButton::WHEEL_UP: +		case MouseButton::WHEEL_DOWN: +		case MouseButton::WHEEL_LEFT: +		case MouseButton::WHEEL_RIGHT: +		case MouseButton::MB_XBUTTON1: +		case MouseButton::MB_XBUTTON2: +			button_string += " (" + RTR(_mouse_button_descriptions[(size_t)idx - 1]) + ")"; // button index starts from 1, array index starts from 0, so subtract 1  			break;  		default:  			break;  	}  	String mods = InputEventWithModifiers::as_text(); -	mods = mods == "" ? TTR("none") : mods; +	mods = mods.is_empty() ? TTR("none") : mods;  	// Work around the fact vformat can only take 5 substitutions but 6 need to be passed.  	String index_and_mods = vformat("button_index=%s, mods=%s", button_index, mods); @@ -773,23 +778,23 @@ String InputEventMouseMotion::as_text() const {  }  String InputEventMouseMotion::to_string() { -	int button_mask = get_button_mask(); -	String button_mask_string = itos(button_mask); -	switch (get_button_mask()) { -		case MOUSE_BUTTON_MASK_LEFT: -			button_mask_string += " (" + RTR(_mouse_button_descriptions[MOUSE_BUTTON_LEFT - 1]) + ")"; +	MouseButton button_mask = get_button_mask(); +	String button_mask_string = itos((int64_t)button_mask); +	switch (button_mask) { +		case MouseButton::MASK_LEFT: +			button_mask_string += " (" + RTR(_mouse_button_descriptions[(size_t)MouseButton::LEFT - 1]) + ")";  			break; -		case MOUSE_BUTTON_MASK_MIDDLE: -			button_mask_string += " (" + RTR(_mouse_button_descriptions[MOUSE_BUTTON_MIDDLE - 1]) + ")"; +		case MouseButton::MASK_MIDDLE: +			button_mask_string += " (" + RTR(_mouse_button_descriptions[(size_t)MouseButton::MIDDLE - 1]) + ")";  			break; -		case MOUSE_BUTTON_MASK_RIGHT: -			button_mask_string += " (" + RTR(_mouse_button_descriptions[MOUSE_BUTTON_RIGHT - 1]) + ")"; +		case MouseButton::MASK_RIGHT: +			button_mask_string += " (" + RTR(_mouse_button_descriptions[(size_t)MouseButton::RIGHT - 1]) + ")";  			break; -		case MOUSE_BUTTON_MASK_XBUTTON1: -			button_mask_string += " (" + RTR(_mouse_button_descriptions[MOUSE_BUTTON_XBUTTON1 - 1]) + ")"; +		case MouseButton::MASK_XBUTTON1: +			button_mask_string += " (" + RTR(_mouse_button_descriptions[(size_t)MouseButton::MB_XBUTTON1 - 1]) + ")";  			break; -		case MOUSE_BUTTON_MASK_XBUTTON2: -			button_mask_string += " (" + RTR(_mouse_button_descriptions[MOUSE_BUTTON_XBUTTON2 - 1]) + ")"; +		case MouseButton::MASK_XBUTTON2: +			button_mask_string += " (" + RTR(_mouse_button_descriptions[(size_t)MouseButton::MB_XBUTTON2 - 1]) + ")";  			break;  		default:  			break; @@ -864,6 +869,8 @@ void InputEventMouseMotion::_bind_methods() {  ///////////////////////////////////  void InputEventJoypadMotion::set_axis(JoyAxis p_axis) { +	ERR_FAIL_COND(p_axis < JoyAxis::LEFT_X || p_axis > JoyAxis::MAX); +  	axis = p_axis;  	emit_changed();  } @@ -928,10 +935,10 @@ bool InputEventJoypadMotion::is_match(const Ref<InputEvent> &p_event, bool p_exa  	}  	return axis == jm->axis && -		   (!p_exact_match || ((axis_value < 0) == (jm->axis_value < 0))); +			(!p_exact_match || ((axis_value < 0) == (jm->axis_value < 0)));  } -static const char *_joy_axis_descriptions[JOY_AXIS_MAX] = { +static const char *_joy_axis_descriptions[(size_t)JoyAxis::MAX] = {  	TTRC("Left Stick X-Axis, Joystick 0 X-Axis"),  	TTRC("Left Stick Y-Axis, Joystick 0 Y-Axis"),  	TTRC("Right Stick X-Axis, Joystick 1 X-Axis"), @@ -945,7 +952,7 @@ static const char *_joy_axis_descriptions[JOY_AXIS_MAX] = {  };  String InputEventJoypadMotion::as_text() const { -	String desc = axis < JOY_AXIS_MAX ? RTR(_joy_axis_descriptions[axis]) : TTR("Unknown Joypad Axis"); +	String desc = axis < JoyAxis::MAX ? RTR(_joy_axis_descriptions[(size_t)axis]) : TTR("Unknown Joypad Axis");  	return vformat(TTR("Joypad Motion on Axis %d (%s) with Value %.2f"), axis, desc, axis_value);  } @@ -1025,7 +1032,7 @@ bool InputEventJoypadButton::is_match(const Ref<InputEvent> &p_event, bool p_exa  	return button_index == button->button_index;  } -static const char *_joy_button_descriptions[JOY_BUTTON_SDL_MAX] = { +static const char *_joy_button_descriptions[(size_t)JoyButton::SDL_MAX] = {  	TTRC("Bottom Action, Sony Cross, Xbox A, Nintendo B"),  	TTRC("Right Action, Sony Circle, Xbox B, Nintendo A"),  	TTRC("Left Action, Sony Square, Xbox X, Nintendo Y"), @@ -1050,10 +1057,10 @@ static const char *_joy_button_descriptions[JOY_BUTTON_SDL_MAX] = {  };  String InputEventJoypadButton::as_text() const { -	String text = "Joypad Button " + itos(button_index); +	String text = "Joypad Button " + itos((int64_t)button_index); -	if (button_index >= 0 && button_index < JOY_BUTTON_SDL_MAX) { -		text += vformat(" (%s)", _joy_button_descriptions[button_index]); +	if (button_index > JoyButton::INVALID && button_index < JoyButton::SDL_MAX) { +		text += vformat(" (%s)", _joy_button_descriptions[(size_t)button_index]);  	}  	if (pressure != 0) { @@ -1499,7 +1506,7 @@ int InputEventMIDI::get_controller_value() const {  }  String InputEventMIDI::as_text() const { -	return vformat(RTR("MIDI Input on Channel=%s Message=%s"), itos(channel), itos(message)); +	return vformat(RTR("MIDI Input on Channel=%s Message=%s"), itos(channel), itos((int64_t)message));  }  String InputEventMIDI::to_string() { @@ -1557,9 +1564,13 @@ bool InputEventShortcut::is_pressed() const {  }  String InputEventShortcut::as_text() const { +	ERR_FAIL_COND_V(shortcut.is_null(), "None"); +  	return vformat(RTR("Input Event with Shortcut=%s"), shortcut->get_as_text());  }  String InputEventShortcut::to_string() { +	ERR_FAIL_COND_V(shortcut.is_null(), "None"); +  	return vformat("InputEventShortcut: shortcut=%s", shortcut->get_as_text());  } diff --git a/core/input/input_event.h b/core/input/input_event.h index 3fc8078a09..70046e4491 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -151,7 +151,7 @@ public:  	void set_modifiers_from_event(const InputEventWithModifiers *event); -	uint32_t get_modifiers_mask() const; +	Key get_modifiers_mask() const;  	virtual String as_text() const override;  	virtual String to_string() override; @@ -164,8 +164,8 @@ class InputEventKey : public InputEventWithModifiers {  	bool pressed = false; /// otherwise release -	Key keycode = KEY_NONE; // Key enum, without modifier masks. -	Key physical_keycode = KEY_NONE; +	Key keycode = Key::NONE; // Key enum, without modifier masks. +	Key physical_keycode = Key::NONE;  	uint32_t unicode = 0; ///unicode  	bool echo = false; /// true if this is an echo key @@ -183,14 +183,14 @@ public:  	void set_physical_keycode(Key p_keycode);  	Key get_physical_keycode() const; -	void set_unicode(uint32_t p_unicode); -	uint32_t get_unicode() const; +	void set_unicode(char32_t p_unicode); +	char32_t get_unicode() const;  	void set_echo(bool p_enable);  	virtual bool is_echo() const override; -	uint32_t get_keycode_with_modifiers() const; -	uint32_t get_physical_keycode_with_modifiers() const; +	Key get_keycode_with_modifiers() const; +	Key get_physical_keycode_with_modifiers() const;  	virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override;  	virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; @@ -208,7 +208,7 @@ public:  class InputEventMouse : public InputEventWithModifiers {  	GDCLASS(InputEventMouse, InputEventWithModifiers); -	int button_mask = 0; +	MouseButton button_mask = MouseButton::NONE;  	Vector2 pos;  	Vector2 global_pos; @@ -217,8 +217,8 @@ protected:  	static void _bind_methods();  public: -	void set_button_mask(int p_mask); -	int get_button_mask() const; +	void set_button_mask(MouseButton p_mask); +	MouseButton get_button_mask() const;  	void set_position(const Vector2 &p_pos);  	Vector2 get_position() const; @@ -233,7 +233,7 @@ class InputEventMouseButton : public InputEventMouse {  	GDCLASS(InputEventMouseButton, InputEventMouse);  	float factor = 1; -	MouseButton button_index = MOUSE_BUTTON_NONE; +	MouseButton button_index = MouseButton::NONE;  	bool pressed = false; //otherwise released  	bool double_click = false; //last even less than double click time @@ -501,7 +501,7 @@ class InputEventMIDI : public InputEvent {  	GDCLASS(InputEventMIDI, InputEvent);  	int channel = 0; -	MIDIMessage message = MIDI_MESSAGE_NONE; +	MIDIMessage message = MIDIMessage::NONE;  	int pitch = 0;  	int velocity = 0;  	int instrument = 0; diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index c7ca65f61a..183a2c3e68 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -33,6 +33,7 @@  #include "core/config/project_settings.h"  #include "core/input/input.h"  #include "core/os/keyboard.h" +#include "core/os/os.h"  InputMap *InputMap::singleton = nullptr; @@ -317,36 +318,36 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {      { "ui_text_dedent",                                TTRC("Dedent") },      { "ui_text_backspace",                             TTRC("Backspace") },      { "ui_text_backspace_word",                        TTRC("Backspace Word") }, -    { "ui_text_backspace_word.OSX",                    TTRC("Backspace Word") }, +    { "ui_text_backspace_word.macos",                  TTRC("Backspace Word") },      { "ui_text_backspace_all_to_left",                 TTRC("Backspace all to Left") }, -    { "ui_text_backspace_all_to_left.OSX",             TTRC("Backspace all to Left") }, +    { "ui_text_backspace_all_to_left.macos",           TTRC("Backspace all to Left") },      { "ui_text_delete",                                TTRC("Delete") },      { "ui_text_delete_word",                           TTRC("Delete Word") }, -    { "ui_text_delete_word.OSX",                       TTRC("Delete Word") }, +    { "ui_text_delete_word.macos",                     TTRC("Delete Word") },      { "ui_text_delete_all_to_right",                   TTRC("Delete all to Right") }, -    { "ui_text_delete_all_to_right.OSX",               TTRC("Delete all to Right") }, +    { "ui_text_delete_all_to_right.macos",             TTRC("Delete all to Right") },      { "ui_text_caret_left",                            TTRC("Caret Left") },      { "ui_text_caret_word_left",                       TTRC("Caret Word Left") }, -    { "ui_text_caret_word_left.OSX",                   TTRC("Caret Word Left") }, +    { "ui_text_caret_word_left.macos",                 TTRC("Caret Word Left") },      { "ui_text_caret_right",                           TTRC("Caret Right") },      { "ui_text_caret_word_right",                      TTRC("Caret Word Right") }, -    { "ui_text_caret_word_right.OSX",                  TTRC("Caret Word Right") }, +    { "ui_text_caret_word_right.macos",                TTRC("Caret Word Right") },      { "ui_text_caret_up",                              TTRC("Caret Up") },      { "ui_text_caret_down",                            TTRC("Caret Down") },      { "ui_text_caret_line_start",                      TTRC("Caret Line Start") }, -    { "ui_text_caret_line_start.OSX",                  TTRC("Caret Line Start") }, +    { "ui_text_caret_line_start.macos",                TTRC("Caret Line Start") },      { "ui_text_caret_line_end",                        TTRC("Caret Line End") }, -    { "ui_text_caret_line_end.OSX",                    TTRC("Caret Line End") }, +    { "ui_text_caret_line_end.macos",                  TTRC("Caret Line End") },      { "ui_text_caret_page_up",                         TTRC("Caret Page Up") },      { "ui_text_caret_page_down",                       TTRC("Caret Page Down") },      { "ui_text_caret_document_start",                  TTRC("Caret Document Start") }, -    { "ui_text_caret_document_start.OSX",              TTRC("Caret Document Start") }, +    { "ui_text_caret_document_start.macos",            TTRC("Caret Document Start") },      { "ui_text_caret_document_end",                    TTRC("Caret Document End") }, -    { "ui_text_caret_document_end.OSX",                TTRC("Caret Document End") }, +    { "ui_text_caret_document_end.macos",              TTRC("Caret Document End") },      { "ui_text_scroll_up",                             TTRC("Scroll Up") }, -    { "ui_text_scroll_up.OSX",                         TTRC("Scroll Up") }, +    { "ui_text_scroll_up.macos",                       TTRC("Scroll Up") },      { "ui_text_scroll_down",                           TTRC("Scroll Down") }, -    { "ui_text_scroll_down.OSX",                       TTRC("Scroll Down") }, +    { "ui_text_scroll_down.macos",                     TTRC("Scroll Down") },      { "ui_text_select_all",                            TTRC("Select All") },      { "ui_text_select_word_under_caret",               TTRC("Select Word Under Caret") },      { "ui_text_toggle_insert_mode",                    TTRC("Toggle Insert Mode") }, @@ -380,353 +381,376 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {  	}  	List<Ref<InputEvent>> inputs; -	inputs.push_back(InputEventKey::create_reference(KEY_ENTER)); -	inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER)); -	inputs.push_back(InputEventKey::create_reference(KEY_SPACE)); +	inputs.push_back(InputEventKey::create_reference(Key::ENTER)); +	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER)); +	inputs.push_back(InputEventKey::create_reference(Key::SPACE));  	default_builtin_cache.insert("ui_accept", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventJoypadButton::create_reference(JOY_BUTTON_Y)); -	inputs.push_back(InputEventKey::create_reference(KEY_SPACE)); +	inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::Y)); +	inputs.push_back(InputEventKey::create_reference(Key::SPACE));  	default_builtin_cache.insert("ui_select", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_ESCAPE)); +	inputs.push_back(InputEventKey::create_reference(Key::ESCAPE));  	default_builtin_cache.insert("ui_cancel", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_TAB)); +	inputs.push_back(InputEventKey::create_reference(Key::TAB));  	default_builtin_cache.insert("ui_focus_next", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_TAB | KEY_MASK_SHIFT)); +	inputs.push_back(InputEventKey::create_reference(Key::TAB | KeyModifierMask::SHIFT));  	default_builtin_cache.insert("ui_focus_prev", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_LEFT)); -	inputs.push_back(InputEventJoypadButton::create_reference(JOY_BUTTON_DPAD_LEFT)); +	inputs.push_back(InputEventKey::create_reference(Key::LEFT)); +	inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_LEFT));  	default_builtin_cache.insert("ui_left", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_RIGHT)); -	inputs.push_back(InputEventJoypadButton::create_reference(JOY_BUTTON_DPAD_RIGHT)); +	inputs.push_back(InputEventKey::create_reference(Key::RIGHT)); +	inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_RIGHT));  	default_builtin_cache.insert("ui_right", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_UP)); -	inputs.push_back(InputEventJoypadButton::create_reference(JOY_BUTTON_DPAD_UP)); +	inputs.push_back(InputEventKey::create_reference(Key::UP)); +	inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_UP));  	default_builtin_cache.insert("ui_up", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_DOWN)); -	inputs.push_back(InputEventJoypadButton::create_reference(JOY_BUTTON_DPAD_DOWN)); +	inputs.push_back(InputEventKey::create_reference(Key::DOWN)); +	inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_DOWN));  	default_builtin_cache.insert("ui_down", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_PAGEUP)); +	inputs.push_back(InputEventKey::create_reference(Key::PAGEUP));  	default_builtin_cache.insert("ui_page_up", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_PAGEDOWN)); +	inputs.push_back(InputEventKey::create_reference(Key::PAGEDOWN));  	default_builtin_cache.insert("ui_page_down", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_HOME)); +	inputs.push_back(InputEventKey::create_reference(Key::HOME));  	default_builtin_cache.insert("ui_home", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_END)); +	inputs.push_back(InputEventKey::create_reference(Key::END));  	default_builtin_cache.insert("ui_end", inputs);  	// ///// UI basic Shortcuts /////  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_X | KEY_MASK_CMD)); -	inputs.push_back(InputEventKey::create_reference(KEY_DELETE | KEY_MASK_SHIFT)); +	inputs.push_back(InputEventKey::create_reference(Key::X | KeyModifierMask::CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::SHIFT));  	default_builtin_cache.insert("ui_cut", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_C | KEY_MASK_CMD)); -	inputs.push_back(InputEventKey::create_reference(KEY_INSERT | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::C | KeyModifierMask::CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_copy", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_V | KEY_MASK_CMD)); -	inputs.push_back(InputEventKey::create_reference(KEY_INSERT | KEY_MASK_SHIFT)); +	inputs.push_back(InputEventKey::create_reference(Key::V | KeyModifierMask::CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::SHIFT));  	default_builtin_cache.insert("ui_paste", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_Z | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::Z | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_undo", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_Z | KEY_MASK_CMD | KEY_MASK_SHIFT)); -	inputs.push_back(InputEventKey::create_reference(KEY_Y | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::Z | KeyModifierMask::CMD | KeyModifierMask::SHIFT)); +	inputs.push_back(InputEventKey::create_reference(Key::Y | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_redo", inputs);  	// ///// UI Text Input Shortcuts /////  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_SPACE | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::SPACE | KeyModifierMask::CTRL));  	default_builtin_cache.insert("ui_text_completion_query", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_ENTER)); -	inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER)); +	inputs.push_back(InputEventKey::create_reference(Key::ENTER)); +	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));  	default_builtin_cache.insert("ui_text_completion_accept", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_TAB)); +	inputs.push_back(InputEventKey::create_reference(Key::TAB));  	default_builtin_cache.insert("ui_text_completion_replace", inputs);  	// Newlines  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_ENTER)); -	inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER)); +	inputs.push_back(InputEventKey::create_reference(Key::ENTER)); +	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));  	default_builtin_cache.insert("ui_text_newline", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_ENTER | KEY_MASK_CMD)); -	inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_text_newline_blank", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_ENTER | KEY_MASK_SHIFT | KEY_MASK_CMD)); -	inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER | KEY_MASK_SHIFT | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::SHIFT | KeyModifierMask::CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::SHIFT | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_text_newline_above", inputs);  	// Indentation  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_TAB)); +	inputs.push_back(InputEventKey::create_reference(Key::TAB));  	default_builtin_cache.insert("ui_text_indent", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_TAB | KEY_MASK_SHIFT)); +	inputs.push_back(InputEventKey::create_reference(Key::TAB | KeyModifierMask::SHIFT));  	default_builtin_cache.insert("ui_text_dedent", inputs);  	// Text Backspace and Delete  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE)); -	inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_SHIFT)); +	inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE)); +	inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::SHIFT));  	default_builtin_cache.insert("ui_text_backspace", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_text_backspace_word", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_ALT)); -	default_builtin_cache.insert("ui_text_backspace_word.OSX", inputs); +	inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::ALT)); +	default_builtin_cache.insert("ui_text_backspace_word.macos", inputs);  	inputs = List<Ref<InputEvent>>();  	default_builtin_cache.insert("ui_text_backspace_all_to_left", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_CMD)); -	default_builtin_cache.insert("ui_text_backspace_all_to_left.OSX", inputs); +	inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::CMD)); +	default_builtin_cache.insert("ui_text_backspace_all_to_left.macos", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_DELETE)); +	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE));  	default_builtin_cache.insert("ui_text_delete", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_DELETE | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_text_delete_word", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_DELETE | KEY_MASK_ALT)); -	default_builtin_cache.insert("ui_text_delete_word.OSX", inputs); +	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::ALT)); +	default_builtin_cache.insert("ui_text_delete_word.macos", inputs);  	inputs = List<Ref<InputEvent>>();  	default_builtin_cache.insert("ui_text_delete_all_to_right", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_DELETE | KEY_MASK_CMD)); -	default_builtin_cache.insert("ui_text_delete_all_to_right.OSX", inputs); +	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::CMD)); +	default_builtin_cache.insert("ui_text_delete_all_to_right.macos", inputs);  	// Text Caret Movement Left/Right  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_LEFT)); +	inputs.push_back(InputEventKey::create_reference(Key::LEFT));  	default_builtin_cache.insert("ui_text_caret_left", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_LEFT | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_text_caret_word_left", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_LEFT | KEY_MASK_ALT)); -	default_builtin_cache.insert("ui_text_caret_word_left.OSX", inputs); +	inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::ALT)); +	default_builtin_cache.insert("ui_text_caret_word_left.macos", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_RIGHT)); +	inputs.push_back(InputEventKey::create_reference(Key::RIGHT));  	default_builtin_cache.insert("ui_text_caret_right", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_RIGHT | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_text_caret_word_right", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_RIGHT | KEY_MASK_ALT)); -	default_builtin_cache.insert("ui_text_caret_word_right.OSX", inputs); +	inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::ALT)); +	default_builtin_cache.insert("ui_text_caret_word_right.macos", inputs);  	// Text Caret Movement Up/Down  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_UP)); +	inputs.push_back(InputEventKey::create_reference(Key::UP));  	default_builtin_cache.insert("ui_text_caret_up", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_DOWN)); +	inputs.push_back(InputEventKey::create_reference(Key::DOWN));  	default_builtin_cache.insert("ui_text_caret_down", inputs);  	// Text Caret Movement Line Start/End  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_HOME)); +	inputs.push_back(InputEventKey::create_reference(Key::HOME));  	default_builtin_cache.insert("ui_text_caret_line_start", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_A | KEY_MASK_CTRL)); -	inputs.push_back(InputEventKey::create_reference(KEY_LEFT | KEY_MASK_CMD)); -	default_builtin_cache.insert("ui_text_caret_line_start.OSX", inputs); +	inputs.push_back(InputEventKey::create_reference(Key::A | KeyModifierMask::CTRL)); +	inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD)); +	default_builtin_cache.insert("ui_text_caret_line_start.macos", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_END)); +	inputs.push_back(InputEventKey::create_reference(Key::END));  	default_builtin_cache.insert("ui_text_caret_line_end", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_E | KEY_MASK_CTRL)); -	inputs.push_back(InputEventKey::create_reference(KEY_RIGHT | KEY_MASK_CMD)); -	default_builtin_cache.insert("ui_text_caret_line_end.OSX", inputs); +	inputs.push_back(InputEventKey::create_reference(Key::E | KeyModifierMask::CTRL)); +	inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD)); +	default_builtin_cache.insert("ui_text_caret_line_end.macos", inputs);  	// Text Caret Movement Page Up/Down  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_PAGEUP)); +	inputs.push_back(InputEventKey::create_reference(Key::PAGEUP));  	default_builtin_cache.insert("ui_text_caret_page_up", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_PAGEDOWN)); +	inputs.push_back(InputEventKey::create_reference(Key::PAGEDOWN));  	default_builtin_cache.insert("ui_text_caret_page_down", inputs);  	// Text Caret Movement Document Start/End  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_HOME | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::HOME | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_text_caret_document_start", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_UP | KEY_MASK_CMD)); -	default_builtin_cache.insert("ui_text_caret_document_start.OSX", inputs); +	inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD)); +	default_builtin_cache.insert("ui_text_caret_document_start.macos", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_END | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::END | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_text_caret_document_end", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_DOWN | KEY_MASK_CMD)); -	default_builtin_cache.insert("ui_text_caret_document_end.OSX", inputs); +	inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD)); +	default_builtin_cache.insert("ui_text_caret_document_end.macos", inputs);  	// Text Scrolling  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_UP | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_text_scroll_up", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_UP | KEY_MASK_CMD | KEY_MASK_ALT)); -	default_builtin_cache.insert("ui_text_scroll_up.OSX", inputs); +	inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD | KeyModifierMask::ALT)); +	default_builtin_cache.insert("ui_text_scroll_up.macos", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_DOWN | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_text_scroll_down", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_DOWN | KEY_MASK_CMD | KEY_MASK_ALT)); -	default_builtin_cache.insert("ui_text_scroll_down.OSX", inputs); +	inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD | KeyModifierMask::ALT)); +	default_builtin_cache.insert("ui_text_scroll_down.macos", inputs);  	// Text Misc  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_A | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::A | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_text_select_all", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_D | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_text_select_word_under_caret", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_INSERT)); +	inputs.push_back(InputEventKey::create_reference(Key::INSERT));  	default_builtin_cache.insert("ui_text_toggle_insert_mode", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_MENU)); +	inputs.push_back(InputEventKey::create_reference(Key::MENU));  	default_builtin_cache.insert("ui_menu", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_ENTER)); -	inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER)); +	inputs.push_back(InputEventKey::create_reference(Key::ENTER)); +	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));  	default_builtin_cache.insert("ui_text_submit", inputs);  	// ///// UI Graph Shortcuts /////  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_D | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_graph_duplicate", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_DELETE)); +	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE));  	default_builtin_cache.insert("ui_graph_delete", inputs);  	// ///// UI File Dialog Shortcuts /////  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE)); +	inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE));  	default_builtin_cache.insert("ui_filedialog_up_one_level", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_F5)); +	inputs.push_back(InputEventKey::create_reference(Key::F5));  	default_builtin_cache.insert("ui_filedialog_refresh", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_H)); +	inputs.push_back(InputEventKey::create_reference(Key::H));  	default_builtin_cache.insert("ui_filedialog_show_hidden", inputs);  	inputs = List<Ref<InputEvent>>(); -	inputs.push_back(InputEventKey::create_reference(KEY_QUOTELEFT | KEY_MASK_CMD)); +	inputs.push_back(InputEventKey::create_reference(Key::QUOTELEFT | KeyModifierMask::CMD));  	default_builtin_cache.insert("ui_swap_input_direction", inputs);  	return default_builtin_cache;  } -void InputMap::load_default() { +const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_feature_overrides_applied() { +	if (default_builtin_with_overrides_cache.size() > 0) { +		return default_builtin_with_overrides_cache; +	} +  	OrderedHashMap<String, List<Ref<InputEvent>>> builtins = get_builtins(); -	// List of Builtins which have an override for OSX. -	Vector<String> osx_builtins; +	// Get a list of all built in inputs which are valid overrides for the OS +	// Key = builtin name (e.g. ui_accept) +	// Value = override/feature names (e.g. macos, if it was defined as "ui_accept.macos" and the platform supports that feature) +	Map<String, Vector<String>> builtins_with_overrides;  	for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) { -		if (String(E.key()).ends_with(".OSX")) { -			// Strip .OSX from name: some_input_name.OSX -> some_input_name -			osx_builtins.push_back(String(E.key()).split(".")[0]); +		String fullname = E.key(); + +		Vector<String> split = fullname.split("."); +		String name = split[0]; +		String override_for = split.size() > 1 ? split[1] : String(); + +		if (!override_for.is_empty() && OS::get_singleton()->has_feature(override_for)) { +			builtins_with_overrides[name].push_back(override_for);  		}  	}  	for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) {  		String fullname = E.key(); -		String name = fullname.split(".")[0]; -		String override_for = fullname.split(".").size() > 1 ? fullname.split(".")[1] : ""; -#ifdef APPLE_STYLE_KEYS -		if (osx_builtins.has(name) && override_for != "OSX") { -			// Name has osx builtin but this particular one is for non-osx systems - so skip. +		Vector<String> split = fullname.split("."); +		String name = split[0]; +		String override_for = split.size() > 1 ? split[1] : String(); + +		if (builtins_with_overrides.has(name) && override_for.is_empty()) { +			// Builtin has an override but this particular one is not an override, so skip.  			continue;  		} -#else -		if (override_for == "OSX") { -			// Override for OSX - not needed on non-osx platforms. + +		if (!override_for.is_empty() && !OS::get_singleton()->has_feature(override_for)) { +			// OS does not support this override - skip.  			continue;  		} -#endif + +		default_builtin_with_overrides_cache.insert(name, E.value()); +	} + +	return default_builtin_with_overrides_cache; +} + +void InputMap::load_default() { +	OrderedHashMap<String, List<Ref<InputEvent>>> builtins = get_builtins_with_feature_overrides_applied(); + +	for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) { +		String name = E.key();  		add_action(name); @@ -746,3 +770,7 @@ InputMap::InputMap() {  	ERR_FAIL_COND_MSG(singleton, "Singleton in InputMap already exist.");  	singleton = this;  } + +InputMap::~InputMap() { +	singleton = nullptr; +} diff --git a/core/input/input_map.h b/core/input/input_map.h index a2d3952f94..0bf572ddca 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -41,8 +41,8 @@ class InputMap : public Object {  public:  	/** -	* A special value used to signify that a given Action can be triggered by any device -	*/ +	 * A special value used to signify that a given Action can be triggered by any device +	 */  	static int ALL_DEVICES;  	struct Action { @@ -56,6 +56,7 @@ private:  	mutable OrderedHashMap<StringName, Action> input_map;  	OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_cache; +	OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache;  	List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; @@ -93,8 +94,10 @@ public:  	String get_builtin_display_name(const String &p_name) const;  	// Use an Ordered Map so insertion order is preserved. We want the elements to be 'grouped' somewhat.  	const OrderedHashMap<String, List<Ref<InputEvent>>> &get_builtins(); +	const OrderedHashMap<String, List<Ref<InputEvent>>> &get_builtins_with_feature_overrides_applied();  	InputMap(); +	~InputMap();  };  #endif // INPUT_MAP_H diff --git a/core/input/shortcut.cpp b/core/input/shortcut.cpp index d0cb08724e..30e35190e4 100644 --- a/core/input/shortcut.cpp +++ b/core/input/shortcut.cpp @@ -31,14 +31,26 @@  #include "shortcut.h"  #include "core/os/keyboard.h" -void Shortcut::set_event(const Ref<InputEvent> &p_event) { -	ERR_FAIL_COND_MSG(Object::cast_to<InputEventShortcut>(*p_event), "Cannot set a shortcut event to an instance of InputEventShortcut."); -	event = p_event; +void Shortcut::set_events(const Array &p_events) { +	for (int i = 0; i < p_events.size(); i++) { +		Ref<InputEventShortcut> ies = p_events[i]; +		ERR_FAIL_COND_MSG(ies.is_valid(), "Cannot set a shortcut event to an instance of InputEventShortcut."); +	} + +	events = p_events;  	emit_changed();  } -Ref<InputEvent> Shortcut::get_event() const { -	return event; +void Shortcut::set_events_list(const List<Ref<InputEvent>> *p_events) { +	events.clear(); + +	for (const Ref<InputEvent> &ie : *p_events) { +		events.push_back(ie); +	} +} + +Array Shortcut::get_events() const { +	return events;  }  bool Shortcut::matches_event(const Ref<InputEvent> &p_event) const { @@ -48,29 +60,73 @@ bool Shortcut::matches_event(const Ref<InputEvent> &p_event) const {  			return true;  		}  	} -	return event.is_valid() && event->is_match(p_event, true); + +	for (int i = 0; i < events.size(); i++) { +		Ref<InputEvent> ie = events[i]; +		bool valid = ie.is_valid() && ie->is_match(p_event); + +		// Stop on first valid event - don't need to check further. +		if (valid) { +			return true; +		} +	} + +	return false;  }  String Shortcut::get_as_text() const { -	if (event.is_valid()) { -		return event->as_text(); -	} else { -		return "None"; +	for (int i = 0; i < events.size(); i++) { +		Ref<InputEvent> ie = events[i]; +		// Return first shortcut which is valid +		if (ie.is_valid()) { +			return ie->as_text(); +		}  	} + +	return "None";  }  bool Shortcut::has_valid_event() const { -	return event.is_valid(); +	// Tests if there is ANY input event which is valid. +	for (int i = 0; i < events.size(); i++) { +		Ref<InputEvent> ie = events[i]; +		if (ie.is_valid()) { +			return true; +		} +	} + +	return false;  }  void Shortcut::_bind_methods() { -	ClassDB::bind_method(D_METHOD("set_event", "event"), &Shortcut::set_event); -	ClassDB::bind_method(D_METHOD("get_event"), &Shortcut::get_event); +	ClassDB::bind_method(D_METHOD("set_events", "events"), &Shortcut::set_events); +	ClassDB::bind_method(D_METHOD("get_events"), &Shortcut::get_events);  	ClassDB::bind_method(D_METHOD("has_valid_event"), &Shortcut::has_valid_event);  	ClassDB::bind_method(D_METHOD("matches_event", "event"), &Shortcut::matches_event);  	ClassDB::bind_method(D_METHOD("get_as_text"), &Shortcut::get_as_text); -	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), "set_event", "get_event"); +	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "events", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")), "set_events", "get_events"); +} + +bool Shortcut::is_event_array_equal(const Array &p_event_array1, const Array &p_event_array2) { +	if (p_event_array1.size() != p_event_array2.size()) { +		return false; +	} + +	bool is_same = true; +	for (int i = 0; i < p_event_array1.size(); i++) { +		Ref<InputEvent> ie_1 = p_event_array1[i]; +		Ref<InputEvent> ie_2 = p_event_array2[i]; + +		is_same = ie_1->is_match(ie_2); + +		// Break on the first that doesn't match - don't need to check further. +		if (!is_same) { +			break; +		} +	} + +	return is_same;  } diff --git a/core/input/shortcut.h b/core/input/shortcut.h index 249dd1971f..a989b10626 100644 --- a/core/input/shortcut.h +++ b/core/input/shortcut.h @@ -37,18 +37,22 @@  class Shortcut : public Resource {  	GDCLASS(Shortcut, Resource); -	Ref<InputEvent> event; +	Array events;  protected:  	static void _bind_methods();  public: -	void set_event(const Ref<InputEvent> &p_shortcut); -	Ref<InputEvent> get_event() const; +	void set_events(const Array &p_events); +	Array get_events() const; + +	void set_events_list(const List<Ref<InputEvent>> *p_events); +  	bool matches_event(const Ref<InputEvent> &p_event) const;  	bool has_valid_event() const;  	String get_as_text() const; -}; +	static bool is_event_array_equal(const Array &p_event_array1, const Array &p_event_array2); +};  #endif // SHORTCUT_H diff --git a/core/io/compression.h b/core/io/compression.h index cbfed74124..06f26876e5 100644 --- a/core/io/compression.h +++ b/core/io/compression.h @@ -54,8 +54,6 @@ public:  	static int get_max_compressed_buffer_size(int p_src_size, Mode p_mode = MODE_ZSTD);  	static int decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size, Mode p_mode = MODE_ZSTD);  	static int decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_size, const uint8_t *p_src, int p_src_size, Mode p_mode); - -	Compression() {}  };  #endif // COMPRESSION_H diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index 49fa73dab2..b2300574f8 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -183,7 +183,9 @@ Error ConfigFile::_internal_save(FileAccess *file) {  		if (E != values.front()) {  			file->store_string("\n");  		} -		file->store_string("[" + E.key() + "]\n\n"); +		if (!E.key().is_empty()) { +			file->store_string("[" + E.key() + "]\n\n"); +		}  		for (OrderedHashMap<String, Variant>::Element F = E.get().front(); F; F = F.next()) {  			String vstr; @@ -285,9 +287,9 @@ Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream)  			return err;  		} -		if (assign != String()) { +		if (!assign.is_empty()) {  			set_value(section, assign, value); -		} else if (next_tag.name != String()) { +		} else if (!next_tag.name.is_empty()) {  			section = next_tag.name;  		}  	} diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp index 8234adea06..d804e67493 100644 --- a/core/io/dir_access.cpp +++ b/core/io/dir_access.cpp @@ -79,7 +79,7 @@ static Error _erase_recursive(DirAccess *da) {  	da->list_dir_begin();  	String n = da->get_next(); -	while (n != String()) { +	while (!n.is_empty()) {  		if (n != "." && n != "..") {  			if (da->current_is_dir()) {  				dirs.push_back(n); @@ -135,7 +135,7 @@ Error DirAccess::make_dir_recursive(String p_dir) {  	String full_dir; -	if (p_dir.is_rel_path()) { +	if (p_dir.is_relative_path()) {  		//append current  		full_dir = get_current_dir().plus_file(p_dir); @@ -183,7 +183,7 @@ String DirAccess::fix_path(String p_path) const {  			if (ProjectSettings::get_singleton()) {  				if (p_path.begins_with("res://")) {  					String resource_path = ProjectSettings::get_singleton()->get_resource_path(); -					if (resource_path != "") { +					if (!resource_path.is_empty()) {  						return p_path.replace_first("res:/", resource_path);  					}  					return p_path.replace_first("res://", ""); @@ -194,7 +194,7 @@ String DirAccess::fix_path(String p_path) const {  		case ACCESS_USERDATA: {  			if (p_path.begins_with("user://")) {  				String data_dir = OS::get_singleton()->get_user_data_dir(); -				if (data_dir != "") { +				if (!data_dir.is_empty()) {  					return p_path.replace_first("user:/", data_dir);  				}  				return p_path.replace_first("user://", ""); @@ -337,7 +337,7 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag  	String curdir = get_current_dir();  	list_dir_begin();  	String n = get_next(); -	while (n != String()) { +	while (!n.is_empty()) {  		if (n != "." && n != "..") {  			if (p_copy_links && is_link(get_current_dir().plus_file(n))) {  				create_link(read_link(get_current_dir().plus_file(n)), p_to + n); @@ -345,7 +345,7 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag  				dirs.push_back(n);  			} else {  				const String &rel_path = n; -				if (!n.is_rel_path()) { +				if (!n.is_relative_path()) {  					list_dir_end();  					return ERR_BUG;  				} diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index e6e79dff8a..1b9c43b155 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -127,7 +127,7 @@ String FileAccess::fix_path(const String &p_path) const {  			if (ProjectSettings::get_singleton()) {  				if (r_path.begins_with("res://")) {  					String resource_path = ProjectSettings::get_singleton()->get_resource_path(); -					if (resource_path != "") { +					if (!resource_path.is_empty()) {  						return r_path.replace("res:/", resource_path);  					}  					return r_path.replace("res://", ""); @@ -138,7 +138,7 @@ String FileAccess::fix_path(const String &p_path) const {  		case ACCESS_USERDATA: {  			if (r_path.begins_with("user://")) {  				String data_dir = OS::get_singleton()->get_user_data_dir(); -				if (data_dir != "") { +				if (!data_dir.is_empty()) {  					return r_path.replace("user:/", data_dir);  				}  				return r_path.replace("user://", ""); diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index e54c947340..df631053b8 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -233,7 +233,7 @@ uint64_t FileAccessCompressed::get_position() const {  	if (writing) {  		return write_pos;  	} else { -		return read_block * block_size + read_pos; +		return (uint64_t)read_block * block_size + read_pos;  	}  } diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 7b43daf9c0..e343706e66 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -110,8 +110,8 @@ PackedData::PackedData() {  }  void PackedData::_free_packed_dirs(PackedDir *p_dir) { -	for (Map<String, PackedDir *>::Element *E = p_dir->subdirs.front(); E; E = E->next()) { -		_free_packed_dirs(E->get()); +	for (const KeyValue<String, PackedDir *> &E : p_dir->subdirs) { +		_free_packed_dirs(E.value);  	}  	memdelete(p_dir);  } @@ -395,8 +395,8 @@ Error DirAccessPack::list_dir_begin() {  	list_dirs.clear();  	list_files.clear(); -	for (Map<String, PackedData::PackedDir *>::Element *E = current->subdirs.front(); E; E = E->next()) { -		list_dirs.push_back(E->key()); +	for (const KeyValue<String, PackedData::PackedDir *> &E : current->subdirs) { +		list_dirs.push_back(E.key);  	}  	for (Set<String>::Element *E = current->files.front(); E; E = E->next()) { @@ -459,7 +459,7 @@ PackedData::PackedDir *DirAccessPack::_find_dir(String p_dir) {  	nd = nd.simplify_path(); -	if (nd == "") { +	if (nd.is_empty()) {  		nd = ".";  	} diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index b5c882e9ce..53bf7456e6 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -99,7 +99,7 @@ static int godot_testerror(voidpf opaque, voidpf stream) {  }  static voidpf godot_alloc(voidpf opaque, uInt items, uInt size) { -	return memalloc(items * size); +	return memalloc((size_t)items * size);  }  static void godot_free(voidpf opaque, voidpf address) { diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 5c1352c1b6..5c67bc4c48 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -49,6 +49,14 @@ HTTPClient *HTTPClient::create() {  	return nullptr;  } +void HTTPClient::set_http_proxy(const String &p_host, int p_port) { +	WARN_PRINT("HTTP proxy feature is not available"); +} + +void HTTPClient::set_https_proxy(const String &p_host, int p_port) { +	WARN_PRINT("HTTPS proxy feature is not available"); +} +  Error HTTPClient::_request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body) {  	int size = p_body.size();  	return request(p_method, p_url, p_headers, size > 0 ? p_body.ptr() : nullptr, size); @@ -85,8 +93,7 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {  			}  		}  	} -	query.erase(0, 1); -	return query; +	return query.substr(1);  }  Dictionary HTTPClient::_get_response_headers_as_dictionary() { @@ -143,6 +150,9 @@ void HTTPClient::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_status"), &HTTPClient::get_status);  	ClassDB::bind_method(D_METHOD("poll"), &HTTPClient::poll); +	ClassDB::bind_method(D_METHOD("set_http_proxy", "host", "port"), &HTTPClient::set_http_proxy); +	ClassDB::bind_method(D_METHOD("set_https_proxy", "host", "port"), &HTTPClient::set_https_proxy); +  	ClassDB::bind_method(D_METHOD("query_string_from_dict", "fields"), &HTTPClient::query_string_from_dict);  	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_mode_enabled"), "set_blocking_mode", "is_blocking_mode_enabled"); diff --git a/core/io/http_client.h b/core/io/http_client.h index 718c3a905e..9bc0134a6a 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -192,6 +192,10 @@ public:  	virtual Error poll() = 0; +	// Use empty string or -1 to unset +	virtual void set_http_proxy(const String &p_host, int p_port); +	virtual void set_https_proxy(const String &p_host, int p_port); +  	HTTPClient() {}  	virtual ~HTTPClient() {}  }; diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp index b3d35b3603..f2ddeddbb8 100644 --- a/core/io/http_client_tcp.cpp +++ b/core/io/http_client_tcp.cpp @@ -70,9 +70,21 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss  	connection = tcp_connection; -	if (conn_host.is_valid_ip_address()) { +	if (ssl && https_proxy_port != -1) { +		proxy_client.instantiate(); // Needs proxy negotiation +		server_host = https_proxy_host; +		server_port = https_proxy_port; +	} else if (!ssl && http_proxy_port != -1) { +		server_host = http_proxy_host; +		server_port = http_proxy_port; +	} else { +		server_host = conn_host; +		server_port = conn_port; +	} + +	if (server_host.is_valid_ip_address()) {  		// Host contains valid IP -		Error err = tcp_connection->connect_to_host(IPAddress(conn_host), p_port); +		Error err = tcp_connection->connect_to_host(IPAddress(server_host), server_port);  		if (err) {  			status = STATUS_CANT_CONNECT;  			return err; @@ -81,7 +93,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss  		status = STATUS_CONNECTING;  	} else {  		// Host contains hostname and needs to be resolved to IP -		resolving = IP::get_singleton()->resolve_hostname_queue_item(conn_host); +		resolving = IP::get_singleton()->resolve_hostname_queue_item(server_host);  		status = STATUS_RESOLVING;  	} @@ -134,7 +146,12 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector<  	ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER);  	ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA); -	String request = String(_methods[p_method]) + " " + p_url + " HTTP/1.1\r\n"; +	String uri = p_url; +	if (!ssl && http_proxy_port != -1) { +		uri = vformat("http://%s:%d%s", conn_host, conn_port, p_url); +	} + +	String request = String(_methods[p_method]) + " " + uri + " HTTP/1.1\r\n";  	bool add_host = true;  	bool add_clen = p_body_size > 0;  	bool add_uagent = true; @@ -229,6 +246,7 @@ void HTTPClientTCP::close() {  	}  	connection.unref(); +	proxy_client.unref();  	status = STATUS_DISCONNECTED;  	head_request = false;  	if (resolving != IP::RESOLVER_INVALID_ID) { @@ -265,7 +283,7 @@ Error HTTPClientTCP::poll() {  					Error err = ERR_BUG; // Should be at least one entry.  					while (ip_candidates.size() > 0) { -						err = tcp_connection->connect_to_host(ip_candidates.front(), conn_port); +						err = tcp_connection->connect_to_host(ip_candidates.pop_front(), server_port);  						if (err == OK) {  							break;  						} @@ -294,7 +312,48 @@ Error HTTPClientTCP::poll() {  					return OK;  				} break;  				case StreamPeerTCP::STATUS_CONNECTED: { -					if (ssl) { +					if (ssl && proxy_client.is_valid()) { +						Error err = proxy_client->poll(); +						if (err == ERR_UNCONFIGURED) { +							proxy_client->set_connection(tcp_connection); +							const Vector<String> headers; +							err = proxy_client->request(METHOD_CONNECT, vformat("%s:%d", conn_host, conn_port), headers, nullptr, 0); +							if (err != OK) { +								status = STATUS_CANT_CONNECT; +								return err; +							} +						} else if (err != OK) { +							status = STATUS_CANT_CONNECT; +							return err; +						} +						switch (proxy_client->get_status()) { +							case STATUS_REQUESTING: { +								return OK; +							} break; +							case STATUS_BODY: { +								proxy_client->read_response_body_chunk(); +								return OK; +							} break; +							case STATUS_CONNECTED: { +								if (proxy_client->get_response_code() != RESPONSE_OK) { +									status = STATUS_CANT_CONNECT; +									return ERR_CANT_CONNECT; +								} +								proxy_client.unref(); +								return OK; +							} +							case STATUS_DISCONNECTED: +							case STATUS_RESOLVING: +							case STATUS_CONNECTING: { +								status = STATUS_CANT_CONNECT; +								ERR_FAIL_V(ERR_BUG); +							} break; +							default: { +								status = STATUS_CANT_CONNECT; +								return ERR_CANT_CONNECT; +							} break; +						} +					} else if (ssl) {  						Ref<StreamPeerSSL> ssl;  						if (!handshaking) {  							// Connect the StreamPeerSSL and start handshaking @@ -344,7 +403,7 @@ Error HTTPClientTCP::poll() {  					Error err = ERR_CANT_CONNECT;  					while (ip_candidates.size() > 0) {  						tcp_connection->disconnect_from_host(); -						err = tcp_connection->connect_to_host(ip_candidates.pop_front(), conn_port); +						err = tcp_connection->connect_to_host(ip_candidates.pop_front(), server_port);  						if (err == OK) {  							return OK;  						} @@ -678,6 +737,26 @@ int HTTPClientTCP::get_read_chunk_size() const {  	return read_chunk_size;  } +void HTTPClientTCP::set_http_proxy(const String &p_host, int p_port) { +	if (p_host.is_empty() || p_port == -1) { +		http_proxy_host = ""; +		http_proxy_port = -1; +	} else { +		http_proxy_host = p_host; +		http_proxy_port = p_port; +	} +} + +void HTTPClientTCP::set_https_proxy(const String &p_host, int p_port) { +	if (p_host.is_empty() || p_port == -1) { +		https_proxy_host = ""; +		https_proxy_port = -1; +	} else { +		https_proxy_host = p_host; +		https_proxy_port = p_port; +	} +} +  HTTPClientTCP::HTTPClientTCP() {  	tcp_connection.instantiate();  } diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h index 170afb551c..40a962925e 100644 --- a/core/io/http_client_tcp.h +++ b/core/io/http_client_tcp.h @@ -38,8 +38,14 @@ private:  	Status status = STATUS_DISCONNECTED;  	IP::ResolverID resolving = IP::RESOLVER_INVALID_ID;  	Array ip_candidates; -	int conn_port = -1; +	int conn_port = -1; // Server to make requests to  	String conn_host; +	int server_port = -1; // Server to connect to (might be a proxy server) +	String server_host; +	int http_proxy_port = -1; // Proxy server for http requests +	String http_proxy_host; +	int https_proxy_port = -1; // Proxy server for https requests +	String https_proxy_host;  	bool ssl = false;  	bool ssl_verify_host = false;  	bool blocking = false; @@ -58,6 +64,7 @@ private:  	Ref<StreamPeerTCP> tcp_connection;  	Ref<StreamPeer> connection; +	Ref<HTTPClientTCP> proxy_client; // Negotiate with proxy server  	int response_num = 0;  	Vector<String> response_headers; @@ -87,6 +94,8 @@ public:  	void set_read_chunk_size(int p_size) override;  	int get_read_chunk_size() const override;  	Error poll() override; +	void set_http_proxy(const String &p_host, int p_port) override; +	void set_https_proxy(const String &p_host, int p_port) override;  	HTTPClientTCP();  }; diff --git a/core/io/image.cpp b/core/io/image.cpp index 3112dd217f..3f34de132f 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -86,20 +86,14 @@ SaveEXRFunc Image::save_exr_func = nullptr;  SavePNGBufferFunc Image::save_png_buffer_func = nullptr; -void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel) { -	uint32_t ofs = (p_y * width + p_x) * p_pixelsize; - -	for (uint32_t i = 0; i < p_pixelsize; i++) { -		p_data[ofs + i] = p_pixel[i]; -	} +void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixel_size, uint8_t *p_data, const uint8_t *p_pixel) { +	uint32_t ofs = (p_y * width + p_x) * p_pixel_size; +	memcpy(p_data + ofs, p_pixel, p_pixel_size);  } -void Image::_get_pixelb(int p_x, int p_y, uint32_t p_pixelsize, const uint8_t *p_data, uint8_t *p_pixel) { -	uint32_t ofs = (p_y * width + p_x) * p_pixelsize; - -	for (uint32_t i = 0; i < p_pixelsize; i++) { -		p_pixel[i] = p_data[ofs + i]; -	} +void Image::_get_pixelb(int p_x, int p_y, uint32_t p_pixel_size, const uint8_t *p_data, uint8_t *p_pixel) { +	uint32_t ofs = (p_y * width + p_x) * p_pixel_size; +	memcpy(p_pixel, p_data + ofs, p_pixel_size);  }  int Image::get_format_pixel_size(Format p_format) { @@ -797,7 +791,7 @@ static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict  					uint32_t interp_down = p01 + (((p11 - p01) * src_xofs_frac) >> FRAC_BITS);  					uint32_t interp = interp_up + (((interp_down - interp_up) * src_yofs_frac) >> FRAC_BITS);  					interp >>= FRAC_BITS; -					p_dst[i * p_dst_width * CC + j * CC + l] = interp; +					p_dst[i * p_dst_width * CC + j * CC + l] = uint8_t(interp);  				} else if (sizeof(T) == 2) { //half float  					float xofs_frac = float(src_xofs_frac) / (1 << FRAC_BITS); @@ -2506,7 +2500,7 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po  		clipped_src_rect.position.y = ABS(p_dest.y);  	} -	if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { +	if (clipped_src_rect.has_no_area()) {  		return;  	} @@ -2561,7 +2555,7 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co  		clipped_src_rect.position.y = ABS(p_dest.y);  	} -	if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { +	if (clipped_src_rect.has_no_area()) {  		return;  	} @@ -2615,7 +2609,7 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P  		clipped_src_rect.position.y = ABS(p_dest.y);  	} -	if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { +	if (clipped_src_rect.has_no_area()) {  		return;  	} @@ -2664,7 +2658,7 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c  		clipped_src_rect.position.y = ABS(p_dest.y);  	} -	if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { +	if (clipped_src_rect.has_no_area()) {  		return;  	} @@ -2697,24 +2691,55 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c  	}  } -void Image::fill(const Color &c) { +// Repeats `p_pixel` `p_count` times in consecutive memory. +// Results in the original pixel and `p_count - 1` subsequent copies of it. +void Image::_repeat_pixel_over_subsequent_memory(uint8_t *p_pixel, int p_pixel_size, int p_count) { +	int offset = 1; +	for (int stride = 1; offset + stride <= p_count; stride *= 2) { +		memcpy(p_pixel + offset * p_pixel_size, p_pixel, stride * p_pixel_size); +		offset += stride; +	} +	if (offset < p_count) { +		memcpy(p_pixel + offset * p_pixel_size, p_pixel, (p_count - offset) * p_pixel_size); +	} +} + +void Image::fill(const Color &p_color) {  	ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot fill in compressed or custom image formats."); -	uint8_t *wp = data.ptrw(); -	uint8_t *dst_data_ptr = wp; +	uint8_t *dst_data_ptr = data.ptrw();  	int pixel_size = get_format_pixel_size(format); -	// put first pixel with the format-aware API -	set_pixel(0, 0, c); +	// Put first pixel with the format-aware API. +	_set_color_at_ofs(dst_data_ptr, 0, p_color); -	for (int y = 0; y < height; y++) { -		for (int x = 0; x < width; x++) { -			uint8_t *dst = &dst_data_ptr[(y * width + x) * pixel_size]; +	_repeat_pixel_over_subsequent_memory(dst_data_ptr, pixel_size, width * height); +} -			for (int k = 0; k < pixel_size; k++) { -				dst[k] = dst_data_ptr[k]; -			} +void Image::fill_rect(const Rect2 &p_rect, const Color &p_color) { +	ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot fill rect in compressed or custom image formats."); + +	Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect.abs()); +	if (r.has_no_area()) { +		return; +	} + +	uint8_t *dst_data_ptr = data.ptrw(); + +	int pixel_size = get_format_pixel_size(format); + +	// Put first pixel with the format-aware API. +	uint8_t *rect_first_pixel_ptr = &dst_data_ptr[(r.position.y * width + r.position.x) * pixel_size]; +	_set_color_at_ofs(rect_first_pixel_ptr, 0, p_color); + +	if (r.size.x == width) { +		// No need to fill rows separately. +		_repeat_pixel_over_subsequent_memory(rect_first_pixel_ptr, pixel_size, width * r.size.y); +	} else { +		_repeat_pixel_over_subsequent_memory(rect_first_pixel_ptr, pixel_size, r.size.x); +		for (int y = 1; y < r.size.y; y++) { +			memcpy(rect_first_pixel_ptr + y * width * pixel_size, rect_first_pixel_ptr, r.size.x * pixel_size);  		}  	}  } @@ -3160,6 +3185,7 @@ void Image::_bind_methods() {  	ClassDB::bind_method(D_METHOD("blend_rect", "src", "src_rect", "dst"), &Image::blend_rect);  	ClassDB::bind_method(D_METHOD("blend_rect_mask", "src", "mask", "src_rect", "dst"), &Image::blend_rect_mask);  	ClassDB::bind_method(D_METHOD("fill", "color"), &Image::fill); +	ClassDB::bind_method(D_METHOD("fill_rect", "rect", "color"), &Image::fill_rect);  	ClassDB::bind_method(D_METHOD("get_used_rect"), &Image::get_used_rect);  	ClassDB::bind_method(D_METHOD("get_rect", "rect"), &Image::get_rect); diff --git a/core/io/image.h b/core/io/image.h index 8f1b251ac3..9023463b08 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -41,7 +41,7 @@   * Image storage class. This is used to store an image in user memory, as well as   * providing some basic methods for image manipulation.   * Images can be loaded from a file, or registered into the Render object as textures. -*/ + */  class Image; @@ -190,8 +190,10 @@ private:  	static int _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1, int *r_mm_width = nullptr, int *r_mm_height = nullptr);  	bool _can_modify(Format p_format) const; -	_FORCE_INLINE_ void _put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel); -	_FORCE_INLINE_ void _get_pixelb(int p_x, int p_y, uint32_t p_pixelsize, const uint8_t *p_data, uint8_t *p_pixel); +	_FORCE_INLINE_ void _put_pixelb(int p_x, int p_y, uint32_t p_pixel_size, uint8_t *p_data, const uint8_t *p_pixel); +	_FORCE_INLINE_ void _get_pixelb(int p_x, int p_y, uint32_t p_pixel_size, const uint8_t *p_data, uint8_t *p_pixel); + +	_FORCE_INLINE_ void _repeat_pixel_over_subsequent_memory(uint8_t *p_pixel, int p_pixel_size, int p_count);  	void _set_data(const Dictionary &p_data);  	Dictionary _get_data() const; @@ -362,7 +364,8 @@ public:  	void blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest);  	void blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest);  	void blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest); -	void fill(const Color &c); +	void fill(const Color &p_color); +	void fill_rect(const Rect2 &p_rect, const Color &p_color);  	Rect2 get_used_rect() const;  	Ref<Image> get_rect(const Rect2 &p_area) const; diff --git a/core/io/ip.cpp b/core/io/ip.cpp index e3102508a3..68b4e4b354 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -288,8 +288,8 @@ Array IP::_get_local_interfaces() const {  	Array results;  	Map<String, Interface_Info> interfaces;  	get_local_interfaces(&interfaces); -	for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) { -		Interface_Info &c = E->get(); +	for (KeyValue<String, Interface_Info> &E : interfaces) { +		Interface_Info &c = E.value;  		Dictionary rc;  		rc["name"] = c.name;  		rc["friendly"] = c.name_friendly; @@ -310,8 +310,8 @@ Array IP::_get_local_interfaces() const {  void IP::get_local_addresses(List<IPAddress> *r_addresses) const {  	Map<String, Interface_Info> interfaces;  	get_local_interfaces(&interfaces); -	for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) { -		for (const IPAddress &F : E->get().ip_addresses) { +	for (const KeyValue<String, Interface_Info> &E : interfaces) { +		for (const IPAddress &F : E.value.ip_addresses) {  			r_addresses->push_front(F);  		}  	} diff --git a/core/io/logger.cpp b/core/io/logger.cpp index 09539f716c..3330bb8149 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -50,7 +50,7 @@ void Logger::set_flush_stdout_on_print(bool value) {  	_flush_stdout_on_print = value;  } -void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) { +void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) {  	if (!should_log(true)) {  		return;  	} @@ -81,7 +81,11 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c  		err_details = p_code;  	} -	logf_error("%s: %s\n", err_type, err_details); +	if (p_editor_notify) { +		logf_error("%s: %s\n", err_type, err_details); +	} else { +		logf_error("USER %s: %s\n", err_type, err_details); +	}  	logf_error("   at: %s (%s:%i) - %s\n", p_function, p_file, p_line, p_code);  } @@ -132,7 +136,7 @@ void RotatedFileLogger::clear_old_backups() {  	da->list_dir_begin();  	String f = da->get_next();  	Set<String> backups; -	while (f != String()) { +	while (!f.is_empty()) {  		if (!da->current_is_dir() && f.begins_with(basename) && f.get_extension() == extension && f != base_path.get_file()) {  			backups.insert(f);  		} @@ -159,7 +163,7 @@ void RotatedFileLogger::rotate_file() {  		if (max_files > 1) {  			String timestamp = Time::get_singleton()->get_datetime_string_from_system().replace(":", ".");  			String backup_name = base_path.get_basename() + timestamp; -			if (base_path.get_extension() != String()) { +			if (!base_path.get_extension().is_empty()) {  				backup_name += "." + base_path.get_extension();  			} @@ -256,13 +260,13 @@ void CompositeLogger::logv(const char *p_format, va_list p_list, bool p_err) {  	}  } -void CompositeLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) { +void CompositeLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) {  	if (!should_log(true)) {  		return;  	}  	for (int i = 0; i < loggers.size(); ++i) { -		loggers[i]->log_error(p_function, p_file, p_line, p_code, p_rationale, p_type); +		loggers[i]->log_error(p_function, p_file, p_line, p_code, p_rationale, p_editor_notify, p_type);  	}  } diff --git a/core/io/logger.h b/core/io/logger.h index ccf68562d6..48b073aa45 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -54,7 +54,7 @@ public:  	static void set_flush_stdout_on_print(bool value);  	virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0 = 0; -	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR); +	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR);  	void logf(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;  	void logf_error(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; @@ -83,7 +83,6 @@ class RotatedFileLogger : public Logger {  	FileAccess *file = nullptr; -	void rotate_file_without_closing();  	void close_file();  	void clear_old_backups();  	void rotate_file(); @@ -103,7 +102,7 @@ public:  	CompositeLogger(Vector<Logger *> p_loggers);  	virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0; -	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR); +	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type = ERR_ERROR);  	void add_logger(Logger *p_logger); diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index e7d5b78d14..7c06a354d1 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -562,7 +562,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int  					return err;  				} -				if (str == String()) { +				if (str.is_empty()) {  					r_variant = (Object *)nullptr;  				} else {  					Object *obj = ClassDB::instantiate(str); diff --git a/core/io/marshalls.h b/core/io/marshalls.h index 05804d5a46..9ea060e48c 100644 --- a/core/io/marshalls.h +++ b/core/io/marshalls.h @@ -44,9 +44,9 @@ typedef uint32_t uintr_t;  #endif  /** -  * Miscellaneous helpers for marshalling data types, and encoding -  * in an endian independent way -  */ + * Miscellaneous helpers for marshalling data types, and encoding + * in an endian independent way + */  union MarshallFloat {  	uint32_t i; ///< int diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp deleted file mode 100644 index c145225751..0000000000 --- a/core/io/multiplayer_api.cpp +++ /dev/null @@ -1,1150 +0,0 @@ -/*************************************************************************/ -/*  multiplayer_api.cpp                                                  */ -/*************************************************************************/ -/*                       This file is part of:                           */ -/*                           GODOT ENGINE                                */ -/*                      https://godotengine.org                          */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */ -/* Copyright (c) 2014-2021 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 "multiplayer_api.h" - -#include "core/debugger/engine_debugger.h" -#include "core/io/marshalls.h" -#include "core/io/multiplayer_replicator.h" -#include "scene/main/node.h" - -#include <stdint.h> - -#ifdef DEBUG_ENABLED -#include "core/os/os.h" -#endif - -String _get_rpc_md5(const Node *p_node) { -	String rpc_list; -	const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods(); -	for (int i = 0; i < node_config.size(); i++) { -		rpc_list += String(node_config[i].name); -	} -	if (p_node->get_script_instance()) { -		const Vector<MultiplayerAPI::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods(); -		for (int i = 0; i < script_config.size(); i++) { -			rpc_list += String(script_config[i].name); -		} -	} -	return rpc_list.md5_text(); -} - -const MultiplayerAPI::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) { -	const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods(); -	for (int i = 0; i < node_config.size(); i++) { -		if (node_config[i].name == p_method) { -			r_id = ((uint16_t)i) | (1 << 15); -			return node_config[i]; -		} -	} -	if (p_node->get_script_instance()) { -		const Vector<MultiplayerAPI::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods(); -		for (int i = 0; i < script_config.size(); i++) { -			if (script_config[i].name == p_method) { -				r_id = (uint16_t)i; -				return script_config[i]; -			} -		} -	} -	return MultiplayerAPI::RPCConfig(); -} - -const MultiplayerAPI::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) { -	Vector<MultiplayerAPI::RPCConfig> config; -	uint16_t id = p_id; -	if (id & (1 << 15)) { -		id = id & ~(1 << 15); -		config = p_node->get_node_rpc_methods(); -	} else if (p_node->get_script_instance()) { -		config = p_node->get_script_instance()->get_rpc_methods(); -	} -	if (id < config.size()) { -		return config[id]; -	} -	return MultiplayerAPI::RPCConfig(); -} - -_FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) { -	switch (mode) { -		case MultiplayerAPI::RPC_MODE_DISABLED: { -			return false; -		} break; -		case MultiplayerAPI::RPC_MODE_ANY: { -			return true; -		} break; -		case MultiplayerAPI::RPC_MODE_AUTHORITY: { -			return !p_node->is_network_authority() && p_remote_id == p_node->get_network_authority(); -		} break; -	} - -	return false; -} - -void MultiplayerAPI::poll() { -	if (!network_peer.is_valid() || network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) { -		return; -	} - -	network_peer->poll(); - -	if (!network_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here. -		return; -	} - -	while (network_peer->get_available_packet_count()) { -		int sender = network_peer->get_packet_peer(); -		const uint8_t *packet; -		int len; - -		Error err = network_peer->get_packet(&packet, len); -		if (err != OK) { -			ERR_PRINT("Error getting packet!"); -			break; // Something is wrong! -		} - -		rpc_sender_id = sender; -		_process_packet(sender, packet, len); -		rpc_sender_id = 0; - -		if (!network_peer.is_valid()) { -			break; // It's also possible that a packet or RPC caused a disconnection, so also check here. -		} -	} -	if (network_peer.is_valid() && network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTED) { -		replicator->poll(); -	} -} - -void MultiplayerAPI::clear() { -	replicator->clear(); -	connected_peers.clear(); -	path_get_cache.clear(); -	path_send_cache.clear(); -	packet_cache.clear(); -	last_send_cache_id = 1; -} - -void MultiplayerAPI::set_root_node(Node *p_node) { -	root_node = p_node; -} - -Node *MultiplayerAPI::get_root_node() { -	return root_node; -} - -void MultiplayerAPI::set_network_peer(const Ref<MultiplayerPeer> &p_peer) { -	if (p_peer == network_peer) { -		return; // Nothing to do -	} - -	ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, -			"Supplied MultiplayerPeer must be connecting or connected."); - -	if (network_peer.is_valid()) { -		network_peer->disconnect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); -		network_peer->disconnect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); -		network_peer->disconnect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); -		network_peer->disconnect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); -		network_peer->disconnect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); -		clear(); -	} - -	network_peer = p_peer; - -	if (network_peer.is_valid()) { -		network_peer->connect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); -		network_peer->connect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); -		network_peer->connect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); -		network_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); -		network_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); -	} -} - -Ref<MultiplayerPeer> MultiplayerAPI::get_network_peer() const { -	return network_peer; -} - -#ifdef DEBUG_ENABLED -void _profile_node_data(const String &p_what, ObjectID p_id) { -	if (EngineDebugger::is_profiling("multiplayer")) { -		Array values; -		values.push_back("node"); -		values.push_back(p_id); -		values.push_back(p_what); -		EngineDebugger::profiler_add_frame_data("multiplayer", values); -	} -} - -void _profile_bandwidth_data(const String &p_inout, int p_size) { -	if (EngineDebugger::is_profiling("multiplayer")) { -		Array values; -		values.push_back("bandwidth"); -		values.push_back(p_inout); -		values.push_back(OS::get_singleton()->get_ticks_msec()); -		values.push_back(p_size); -		EngineDebugger::profiler_add_frame_data("multiplayer", values); -	} -} -#endif - -// Returns the packet size stripping the node path added when the node is not yet cached. -int get_packet_len(uint32_t p_node_target, int p_packet_len) { -	if (p_node_target & 0x80000000) { -		int ofs = p_node_target & 0x7FFFFFFF; -		return p_packet_len - (p_packet_len - ofs); -	} else { -		return p_packet_len; -	} -} - -void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { -	ERR_FAIL_COND_MSG(root_node == nullptr, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it."); -	ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small."); - -#ifdef DEBUG_ENABLED -	_profile_bandwidth_data("in", p_packet_len); -#endif - -	// Extract the `packet_type` from the LSB three bits: -	uint8_t packet_type = p_packet[0] & 7; - -	switch (packet_type) { -		case NETWORK_COMMAND_SIMPLIFY_PATH: { -			_process_simplify_path(p_from, p_packet, p_packet_len); -		} break; - -		case NETWORK_COMMAND_CONFIRM_PATH: { -			_process_confirm_path(p_from, p_packet, p_packet_len); -		} break; - -		case NETWORK_COMMAND_REMOTE_CALL: { -			// Extract packet meta -			int packet_min_size = 1; -			int name_id_offset = 1; -			ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); -			// Compute the meta size, which depends on the compression level. -			int node_id_compression = (p_packet[0] & 24) >> NODE_ID_COMPRESSION_SHIFT; -			int name_id_compression = (p_packet[0] & 32) >> NAME_ID_COMPRESSION_SHIFT; - -			switch (node_id_compression) { -				case NETWORK_NODE_ID_COMPRESSION_8: -					packet_min_size += 1; -					name_id_offset += 1; -					break; -				case NETWORK_NODE_ID_COMPRESSION_16: -					packet_min_size += 2; -					name_id_offset += 2; -					break; -				case NETWORK_NODE_ID_COMPRESSION_32: -					packet_min_size += 4; -					name_id_offset += 4; -					break; -				default: -					ERR_FAIL_MSG("Was not possible to extract the node id compression mode."); -			} -			switch (name_id_compression) { -				case NETWORK_NAME_ID_COMPRESSION_8: -					packet_min_size += 1; -					break; -				case NETWORK_NAME_ID_COMPRESSION_16: -					packet_min_size += 2; -					break; -				default: -					ERR_FAIL_MSG("Was not possible to extract the name id compression mode."); -			} -			ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); - -			uint32_t node_target = 0; -			switch (node_id_compression) { -				case NETWORK_NODE_ID_COMPRESSION_8: -					node_target = p_packet[1]; -					break; -				case NETWORK_NODE_ID_COMPRESSION_16: -					node_target = decode_uint16(p_packet + 1); -					break; -				case NETWORK_NODE_ID_COMPRESSION_32: -					node_target = decode_uint32(p_packet + 1); -					break; -				default: -					// Unreachable, checked before. -					CRASH_NOW(); -			} - -			Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len); -			ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found."); - -			uint16_t name_id = 0; -			switch (name_id_compression) { -				case NETWORK_NAME_ID_COMPRESSION_8: -					name_id = p_packet[name_id_offset]; -					break; -				case NETWORK_NAME_ID_COMPRESSION_16: -					name_id = decode_uint16(p_packet + name_id_offset); -					break; -				default: -					// Unreachable, checked before. -					CRASH_NOW(); -			} - -			const int packet_len = get_packet_len(node_target, p_packet_len); -			_process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size); -		} break; - -		case NETWORK_COMMAND_RAW: { -			_process_raw(p_from, p_packet, p_packet_len); -		} break; -		case NETWORK_COMMAND_SPAWN: { -			replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, true); -		} break; -		case NETWORK_COMMAND_DESPAWN: { -			replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, false); -		} break; -		case NETWORK_COMMAND_SYNC: { -			replicator->process_sync(p_from, p_packet, p_packet_len); -		} break; -	} -} - -Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) { -	Node *node = nullptr; - -	if (p_node_target & 0x80000000) { -		// Use full path (not cached yet). -		int ofs = p_node_target & 0x7FFFFFFF; - -		ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared."); - -		String paths; -		paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs); - -		NodePath np = paths; - -		node = root_node->get_node(np); - -		if (!node) { -			ERR_PRINT("Failed to get path from RPC: " + String(np) + "."); -		} -		return node; -	} else { -		// Use cached path. -		return get_cached_node(p_from, p_node_target); -	} -} - -void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { -	ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small."); - -	// Check that remote can call the RPC on this node. -	const RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id); -	ERR_FAIL_COND(config.name == StringName()); - -	bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from); -	ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_network_authority()) + "."); - -	int argc = 0; -	bool byte_only = false; - -	const bool byte_only_or_no_args = ((p_packet[0] & 64) >> BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1; -	if (byte_only_or_no_args) { -		if (p_offset < p_packet_len) { -			// This packet contains only bytes. -			argc = 1; -			byte_only = true; -		} else { -			// This rpc calls a method without parameters. -		} -	} else { -		// Normal variant, takes the argument count from the packet. -		ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); -		argc = p_packet[p_offset]; -		p_offset += 1; -	} - -	Vector<Variant> args; -	Vector<const Variant *> argp; -	args.resize(argc); -	argp.resize(argc); - -#ifdef DEBUG_ENABLED -	_profile_node_data("in_rpc", p_node->get_instance_id()); -#endif - -	if (byte_only) { -		Vector<uint8_t> pure_data; -		const int len = p_packet_len - p_offset; -		pure_data.resize(len); -		memcpy(pure_data.ptrw(), &p_packet[p_offset], len); -		args.write[0] = pure_data; -		argp.write[0] = &args[0]; -		p_offset += len; -	} else { -		for (int i = 0; i < argc; i++) { -			ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); - -			int vlen; -			Error err = decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); -			ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument."); - -			argp.write[i] = &args[i]; -			p_offset += vlen; -		} -	} - -	Callable::CallError ce; - -	p_node->call(config.name, (const Variant **)argp.ptr(), argc, ce); -	if (ce.error != Callable::CallError::CALL_OK) { -		String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce); -		error = "RPC - " + error; -		ERR_PRINT(error); -	} -} - -void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { -	ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small."); -	int ofs = 1; - -	String methods_md5; -	methods_md5.parse_utf8((const char *)(p_packet + ofs), 32); -	ofs += 33; - -	int id = decode_uint32(&p_packet[ofs]); -	ofs += 4; - -	String paths; -	paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs); - -	NodePath path = paths; - -	if (!path_get_cache.has(p_from)) { -		path_get_cache[p_from] = PathGetCache(); -	} - -	Node *node = root_node->get_node(path); -	ERR_FAIL_COND(node == nullptr); -	const bool valid_rpc_checksum = _get_rpc_md5(node) == methods_md5; -	if (valid_rpc_checksum == false) { -		ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); -	} - -	PathGetCache::NodeInfo ni; -	ni.path = path; - -	path_get_cache[p_from].nodes[id] = ni; - -	// Encode path to send ack. -	CharString pname = String(path).utf8(); -	int len = encode_cstring(pname.get_data(), nullptr); - -	Vector<uint8_t> packet; - -	packet.resize(1 + 1 + len); -	packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH; -	packet.write[1] = valid_rpc_checksum; -	encode_cstring(pname.get_data(), &packet.write[2]); - -	network_peer->set_transfer_channel(0); -	network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); -	network_peer->set_target_peer(p_from); -	network_peer->put_packet(packet.ptr(), packet.size()); -} - -void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { -	ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small."); - -	const bool valid_rpc_checksum = p_packet[1]; - -	String paths; -	paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2); - -	NodePath path = paths; - -	if (valid_rpc_checksum == false) { -		ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); -	} - -	PathSentCache *psc = path_send_cache.getptr(path); -	ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache."); - -	Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from); -	ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path."); -	E->get() = true; -} - -bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) { -	bool has_all_peers = true; -	List<int> peers_to_add; // If one is missing, take note to add it. - -	for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { -		if (p_target < 0 && E->get() == -p_target) { -			continue; // Continue, excluded. -		} - -		if (p_target > 0 && E->get() != p_target) { -			continue; // Continue, not for this peer. -		} - -		Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); - -		if (!F || !F->get()) { -			// Path was not cached, or was cached but is unconfirmed. -			if (!F) { -				// Not cached at all, take note. -				peers_to_add.push_back(E->get()); -			} - -			has_all_peers = false; -		} -	} - -	if (peers_to_add.size() > 0) { -		// Those that need to be added, send a message for this. - -		// Encode function name. -		const CharString path = String(p_path).utf8(); -		const int path_len = encode_cstring(path.get_data(), nullptr); - -		// Extract MD5 from rpc methods list. -		const String methods_md5 = _get_rpc_md5(p_node); -		const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder. - -		Vector<uint8_t> packet; -		packet.resize(1 + 4 + path_len + methods_md5_len); -		int ofs = 0; - -		packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH; -		ofs += 1; - -		ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]); - -		ofs += encode_uint32(psc->id, &packet.write[ofs]); - -		ofs += encode_cstring(path.get_data(), &packet.write[ofs]); - -		for (int &E : peers_to_add) { -			network_peer->set_target_peer(E); // To all of you. -			network_peer->set_transfer_channel(0); -			network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); -			network_peer->put_packet(packet.ptr(), packet.size()); - -			psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed. -		} -	} - -	return has_all_peers; -} - -// The variant is compressed and encoded; The first byte contains all the meta -// information and the format is: -// - The first LSB 5 bits are used for the variant type. -// - The next two bits are used to store the encoding mode. -// - The most significant is used to store the boolean value. -#define VARIANT_META_TYPE_MASK 0x1F -#define VARIANT_META_EMODE_MASK 0x60 -#define VARIANT_META_BOOL_MASK 0x80 -#define ENCODE_8 0 << 5 -#define ENCODE_16 1 << 5 -#define ENCODE_32 2 << 5 -#define ENCODE_64 3 << 5 -Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) { -	// Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31 -	CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK); - -	uint8_t *buf = r_buffer; -	r_len = 0; -	uint8_t encode_mode = 0; - -	switch (p_variant.get_type()) { -		case Variant::BOOL: { -			if (buf) { -				// We still have 1 free bit in the meta, so let's use it. -				buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0; -				buf[0] |= encode_mode | p_variant.get_type(); -			} -			r_len += 1; -		} break; -		case Variant::INT: { -			if (buf) { -				// Reserve the first byte for the meta. -				buf += 1; -			} -			r_len += 1; -			int64_t val = p_variant; -			if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) { -				// Use 8 bit -				encode_mode = ENCODE_8; -				if (buf) { -					buf[0] = val; -				} -				r_len += 1; -			} else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) { -				// Use 16 bit -				encode_mode = ENCODE_16; -				if (buf) { -					encode_uint16(val, buf); -				} -				r_len += 2; -			} else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) { -				// Use 32 bit -				encode_mode = ENCODE_32; -				if (buf) { -					encode_uint32(val, buf); -				} -				r_len += 4; -			} else { -				// Use 64 bit -				encode_mode = ENCODE_64; -				if (buf) { -					encode_uint64(val, buf); -				} -				r_len += 8; -			} -			// Store the meta -			if (buf) { -				buf -= 1; -				buf[0] = encode_mode | p_variant.get_type(); -			} -		} break; -		default: -			// Any other case is not yet compressed. -			Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding); -			if (err != OK) { -				return err; -			} -			if (r_buffer) { -				// The first byte is not used by the marshalling, so store the type -				// so we know how to decompress and decode this variant. -				r_buffer[0] = p_variant.get_type(); -			} -	} - -	return OK; -} - -Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) { -	const uint8_t *buf = p_buffer; -	int len = p_len; - -	ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); -	uint8_t type = buf[0] & VARIANT_META_TYPE_MASK; -	uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK; - -	ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA); - -	switch (type) { -		case Variant::BOOL: { -			bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0; -			r_variant = val; -			if (r_len) { -				*r_len = 1; -			} -		} break; -		case Variant::INT: { -			buf += 1; -			len -= 1; -			if (r_len) { -				*r_len = 1; -			} -			if (encode_mode == ENCODE_8) { -				// 8 bits. -				ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); -				int8_t val = buf[0]; -				r_variant = val; -				if (r_len) { -					(*r_len) += 1; -				} -			} else if (encode_mode == ENCODE_16) { -				// 16 bits. -				ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA); -				int16_t val = decode_uint16(buf); -				r_variant = val; -				if (r_len) { -					(*r_len) += 2; -				} -			} else if (encode_mode == ENCODE_32) { -				// 32 bits. -				ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); -				int32_t val = decode_uint32(buf); -				r_variant = val; -				if (r_len) { -					(*r_len) += 4; -				} -			} else { -				// 64 bits. -				ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); -				int64_t val = decode_uint64(buf); -				r_variant = val; -				if (r_len) { -					(*r_len) += 8; -				} -			} -		} break; -		default: -			Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding); -			if (err != OK) { -				return err; -			} -	} - -	return OK; -} - -void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) { -	ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree."); - -	ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree."); - -	ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected."); - -	ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments >255."); - -	if (p_to != 0 && !connected_peers.has(ABS(p_to))) { -		ERR_FAIL_COND_MSG(p_to == network_peer->get_unique_id(), "Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()) + "."); - -		ERR_FAIL_MSG("Attempt to remote call unexisting ID: " + itos(p_to) + "."); -	} - -	NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path()); -	ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!"); - -	// See if the path is cached. -	PathSentCache *psc = path_send_cache.getptr(from_path); -	if (!psc) { -		// Path is not cached, create. -		path_send_cache[from_path] = PathSentCache(); -		psc = path_send_cache.getptr(from_path); -		psc->id = last_send_cache_id++; -	} - -	// See if all peers have cached path (if so, call can be fast). -	const bool has_all_peers = _send_confirm_path(p_from, from_path, psc, p_to); - -	// Create base packet, lots of hardcode because it must be tight. - -	int ofs = 0; - -#define MAKE_ROOM(m_amount)             \ -	if (packet_cache.size() < m_amount) \ -		packet_cache.resize(m_amount); - -	// Encode meta. -	// The meta is composed by a single byte that contains (starting from the least significant bit): -	// - `NetworkCommands` in the first three bits. -	// - `NetworkNodeIdCompression` in the next 2 bits. -	// - `NetworkNameIdCompression` in the next 1 bit. -	// - `byte_only_or_no_args` in the next 1 bit. -	// - So we still have the last bit free! -	uint8_t command_type = NETWORK_COMMAND_REMOTE_CALL; -	uint8_t node_id_compression = UINT8_MAX; -	uint8_t name_id_compression = UINT8_MAX; -	bool byte_only_or_no_args = false; - -	MAKE_ROOM(1); -	// The meta is composed along the way, so just set 0 for now. -	packet_cache.write[0] = 0; -	ofs += 1; - -	// Encode Node ID. -	if (has_all_peers) { -		// Compress the node ID only if all the target peers already know it. -		if (psc->id >= 0 && psc->id <= 255) { -			// We can encode the id in 1 byte -			node_id_compression = NETWORK_NODE_ID_COMPRESSION_8; -			MAKE_ROOM(ofs + 1); -			packet_cache.write[ofs] = static_cast<uint8_t>(psc->id); -			ofs += 1; -		} else if (psc->id >= 0 && psc->id <= 65535) { -			// We can encode the id in 2 bytes -			node_id_compression = NETWORK_NODE_ID_COMPRESSION_16; -			MAKE_ROOM(ofs + 2); -			encode_uint16(static_cast<uint16_t>(psc->id), &(packet_cache.write[ofs])); -			ofs += 2; -		} else { -			// Too big, let's use 4 bytes. -			node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; -			MAKE_ROOM(ofs + 4); -			encode_uint32(psc->id, &(packet_cache.write[ofs])); -			ofs += 4; -		} -	} else { -		// The targets don't know the node yet, so we need to use 32 bits int. -		node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; -		MAKE_ROOM(ofs + 4); -		encode_uint32(psc->id, &(packet_cache.write[ofs])); -		ofs += 4; -	} - -	// Encode method ID -	if (p_rpc_id <= UINT8_MAX) { -		// The ID fits in 1 byte -		name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; -		MAKE_ROOM(ofs + 1); -		packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id); -		ofs += 1; -	} else { -		// The ID is larger, let's use 2 bytes -		name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; -		MAKE_ROOM(ofs + 2); -		encode_uint16(p_rpc_id, &(packet_cache.write[ofs])); -		ofs += 2; -	} - -	if (p_argcount == 0) { -		byte_only_or_no_args = true; -	} else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) { -		byte_only_or_no_args = true; -		// Special optimization when only the byte vector is sent. -		const Vector<uint8_t> data = *p_arg[0]; -		MAKE_ROOM(ofs + data.size()); -		memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size()); -		ofs += data.size(); -	} else { -		// Arguments -		MAKE_ROOM(ofs + 1); -		packet_cache.write[ofs] = p_argcount; -		ofs += 1; -		for (int i = 0; i < p_argcount; i++) { -			int len(0); -			Error err = encode_and_compress_variant(*p_arg[i], nullptr, len); -			ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); -			MAKE_ROOM(ofs + len); -			encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len); -			ofs += len; -		} -	} - -	ERR_FAIL_COND(command_type > 7); -	ERR_FAIL_COND(node_id_compression > 3); -	ERR_FAIL_COND(name_id_compression > 1); - -	// We can now set the meta -	packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + ((byte_only_or_no_args ? 1 : 0) << BYTE_ONLY_OR_NO_ARGS_SHIFT); - -#ifdef DEBUG_ENABLED -	_profile_bandwidth_data("out", ofs); -#endif - -	// Take chance and set transfer mode, since all send methods will use it. -	network_peer->set_transfer_channel(p_config.channel); -	network_peer->set_transfer_mode(p_config.transfer_mode); - -	if (has_all_peers) { -		// They all have verified paths, so send fast. -		network_peer->set_target_peer(p_to); // To all of you. -		network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love. -	} else { -		// Unreachable because the node ID is never compressed if the peers doesn't know it. -		CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32); - -		// Not all verified path, so send one by one. - -		// Append path at the end, since we will need it for some packets. -		CharString pname = String(from_path).utf8(); -		int path_len = encode_cstring(pname.get_data(), nullptr); -		MAKE_ROOM(ofs + path_len); -		encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); - -		for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { -			if (p_to < 0 && E->get() == -p_to) { -				continue; // Continue, excluded. -			} - -			if (p_to > 0 && E->get() != p_to) { -				continue; // Continue, not for this peer. -			} - -			Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); -			ERR_CONTINUE(!F); // Should never happen. - -			network_peer->set_target_peer(E->get()); // To this one specifically. - -			if (F->get()) { -				// This one confirmed path, so use id. -				encode_uint32(psc->id, &(packet_cache.write[1])); -				network_peer->put_packet(packet_cache.ptr(), ofs); -			} else { -				// This one did not confirm path yet, so use entire path (sorry!). -				encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag. -				network_peer->put_packet(packet_cache.ptr(), ofs + path_len); -			} -		} -	} -} - -void MultiplayerAPI::_add_peer(int p_id) { -	connected_peers.insert(p_id); -	path_get_cache.insert(p_id, PathGetCache()); -	if (is_network_server()) { -		replicator->spawn_all(p_id); -	} -	emit_signal(SNAME("network_peer_connected"), p_id); -} - -void MultiplayerAPI::_del_peer(int p_id) { -	connected_peers.erase(p_id); -	// Cleanup get cache. -	path_get_cache.erase(p_id); -	// Cleanup sent cache. -	// Some refactoring is needed to make this faster and do paths GC. -	List<NodePath> keys; -	path_send_cache.get_key_list(&keys); -	for (const NodePath &E : keys) { -		PathSentCache *psc = path_send_cache.getptr(E); -		psc->confirmed_peers.erase(p_id); -	} -	emit_signal(SNAME("network_peer_disconnected"), p_id); -} - -void MultiplayerAPI::_connected_to_server() { -	emit_signal(SNAME("connected_to_server")); -} - -void MultiplayerAPI::_connection_failed() { -	emit_signal(SNAME("connection_failed")); -} - -void MultiplayerAPI::_server_disconnected() { -	emit_signal(SNAME("server_disconnected")); -} - -void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { -	ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active."); -	ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); -	ERR_FAIL_COND_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected."); - -	int node_id = network_peer->get_unique_id(); -	bool call_local_native = false; -	bool call_local_script = false; -	uint16_t rpc_id = UINT16_MAX; -	const RPCConfig config = _get_rpc_config(p_node, p_method, rpc_id); -	ERR_FAIL_COND_MSG(config.name == StringName(), -			vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, p_node->get_path())); -	if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { -		if (rpc_id & (1 << 15)) { -			call_local_native = config.sync; -		} else { -			call_local_script = config.sync; -		} -	} - -	if (p_peer_id != node_id) { -#ifdef DEBUG_ENABLED -		_profile_node_data("out_rpc", p_node->get_instance_id()); -#endif - -		_send_rpc(p_node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount); -	} - -	if (call_local_native) { -		int temp_id = rpc_sender_id; -		rpc_sender_id = get_network_unique_id(); -		Callable::CallError ce; -		p_node->call(p_method, p_arg, p_argcount, ce); -		rpc_sender_id = temp_id; -		if (ce.error != Callable::CallError::CALL_OK) { -			String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); -			error = "rpc() aborted in local call:  - " + error + "."; -			ERR_PRINT(error); -			return; -		} -	} - -	if (call_local_script) { -		int temp_id = rpc_sender_id; -		rpc_sender_id = get_network_unique_id(); -		Callable::CallError ce; -		ce.error = Callable::CallError::CALL_OK; -		p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce); -		rpc_sender_id = temp_id; -		if (ce.error != Callable::CallError::CALL_OK) { -			String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); -			error = "rpc() aborted in script local call:  - " + error + "."; -			ERR_PRINT(error); -			return; -		} -	} - -	ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.sync, "RPC '" + p_method + "' on yourself is not allowed by selected mode."); -} - -Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, MultiplayerPeer::TransferMode p_mode, int p_channel) { -	ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet."); -	ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active."); -	ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected."); - -	MAKE_ROOM(p_data.size() + 1); -	const uint8_t *r = p_data.ptr(); -	packet_cache.write[0] = NETWORK_COMMAND_RAW; -	memcpy(&packet_cache.write[1], &r[0], p_data.size()); - -	network_peer->set_target_peer(p_to); -	network_peer->set_transfer_channel(p_channel); -	network_peer->set_transfer_mode(p_mode); - -	return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); -} - -void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { -	ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); - -	Vector<uint8_t> out; -	int len = p_packet_len - 1; -	out.resize(len); -	{ -		uint8_t *w = out.ptrw(); -		memcpy(&w[0], &p_packet[1], len); -	} -	emit_signal(SNAME("network_peer_packet"), p_from, out); -} - -bool MultiplayerAPI::send_confirm_path(Node *p_node, NodePath p_path, int p_peer_id, int &r_id) { -	// See if the path is cached. -	PathSentCache *psc = path_send_cache.getptr(p_path); -	if (!psc) { -		// Path is not cached, create. -		path_send_cache[p_path] = PathSentCache(); -		psc = path_send_cache.getptr(p_path); -		psc->id = last_send_cache_id++; -	} -	r_id = psc->id; - -	// See if all peers have cached path (if so, call can be fast). -	return _send_confirm_path(p_node, p_path, psc, p_peer_id); -} - -Node *MultiplayerAPI::get_cached_node(int p_from, uint32_t p_node_id) { -	Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); -	ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from)); - -	Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(p_node_id); -	ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_node_id, p_from)); - -	PathGetCache::NodeInfo *ni = &F->get(); -	Node *node = root_node->get_node(ni->path); -	if (!node) { -		ERR_PRINT("Failed to get cached path: " + String(ni->path) + "."); -	} -	return node; -} - -int MultiplayerAPI::get_network_unique_id() const { -	ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), 0, "No network peer is assigned. Unable to get unique network ID."); -	return network_peer->get_unique_id(); -} - -bool MultiplayerAPI::is_network_server() const { -	return network_peer.is_valid() && network_peer->is_server(); -} - -void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { -	ERR_FAIL_COND_MSG(!network_peer.is_valid(), "No network peer is assigned. Unable to set 'refuse_new_connections'."); -	network_peer->set_refuse_new_connections(p_refuse); -} - -bool MultiplayerAPI::is_refusing_new_network_connections() const { -	ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. Unable to get 'refuse_new_connections'."); -	return network_peer->is_refusing_new_connections(); -} - -Vector<int> MultiplayerAPI::get_network_connected_peers() const { -	ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), Vector<int>(), "No network peer is assigned. Assume no peers are connected."); - -	Vector<int> ret; -	for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { -		ret.push_back(E->get()); -	} - -	return ret; -} - -void MultiplayerAPI::set_allow_object_decoding(bool p_enable) { -	allow_object_decoding = p_enable; -} - -bool MultiplayerAPI::is_object_decoding_allowed() const { -	return allow_object_decoding; -} - -MultiplayerReplicator *MultiplayerAPI::get_replicator() const { -	return replicator; -} - -void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) { -	replicator->scene_enter_exit_notify(p_scene, p_node, p_enter); -} - -void MultiplayerAPI::_bind_methods() { -	ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); -	ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node); -	ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); -	ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer); -	ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer); -	ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id); -	ClassDB::bind_method(D_METHOD("is_network_server"), &MultiplayerAPI::is_network_server); -	ClassDB::bind_method(D_METHOD("get_rpc_sender_id"), &MultiplayerAPI::get_rpc_sender_id); -	ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &MultiplayerAPI::set_network_peer); -	ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll); -	ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear); - -	ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &MultiplayerAPI::get_network_connected_peers); -	ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &MultiplayerAPI::set_refuse_new_network_connections); -	ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections); -	ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding); -	ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed); -	ClassDB::bind_method(D_METHOD("get_replicator"), &MultiplayerAPI::get_replicator); - -	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); -	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections"); -	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_network_peer", "get_network_peer"); -	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node"); -	ADD_PROPERTY_DEFAULT("refuse_new_network_connections", false); -	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replicator", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerReplicator", PROPERTY_USAGE_NONE), "", "get_replicator"); - -	ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id"))); -	ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id"))); -	ADD_SIGNAL(MethodInfo("network_peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet"))); -	ADD_SIGNAL(MethodInfo("connected_to_server")); -	ADD_SIGNAL(MethodInfo("connection_failed")); -	ADD_SIGNAL(MethodInfo("server_disconnected")); - -	BIND_ENUM_CONSTANT(RPC_MODE_DISABLED); -	BIND_ENUM_CONSTANT(RPC_MODE_ANY); -	BIND_ENUM_CONSTANT(RPC_MODE_AUTHORITY); -} - -MultiplayerAPI::MultiplayerAPI() { -	replicator = memnew(MultiplayerReplicator(this)); -	clear(); -} - -MultiplayerAPI::~MultiplayerAPI() { -	clear(); -	memdelete(replicator); -} diff --git a/core/io/packed_data_container.cpp b/core/io/packed_data_container.cpp index 4a76f0191d..d34b5b6fe3 100644 --- a/core/io/packed_data_container.cpp +++ b/core/io/packed_data_container.cpp @@ -100,6 +100,7 @@ Variant PackedDataContainer::_iter_get_ofs(const Variant &p_iter, uint32_t p_off  }  Variant PackedDataContainer::_get_at_ofs(uint32_t p_ofs, const uint8_t *p_buf, bool &err) const { +	ERR_FAIL_COND_V(p_ofs + 4 > (uint32_t)data.size(), Variant());  	uint32_t type = decode_uint32(p_buf + p_ofs);  	if (type == TYPE_ARRAY || type == TYPE_DICT) { @@ -122,6 +123,7 @@ Variant PackedDataContainer::_get_at_ofs(uint32_t p_ofs, const uint8_t *p_buf, b  }  uint32_t PackedDataContainer::_type_at_ofs(uint32_t p_ofs) const { +	ERR_FAIL_COND_V(p_ofs + 4 > (uint32_t)data.size(), 0);  	const uint8_t *rd = data.ptr();  	ERR_FAIL_COND_V(!rd, 0);  	const uint8_t *r = &rd[p_ofs]; @@ -131,6 +133,7 @@ uint32_t PackedDataContainer::_type_at_ofs(uint32_t p_ofs) const {  }  int PackedDataContainer::_size(uint32_t p_ofs) const { +	ERR_FAIL_COND_V(p_ofs + 4 > (uint32_t)data.size(), 0);  	const uint8_t *rd = data.ptr();  	ERR_FAIL_COND_V(!rd, 0);  	const uint8_t *r = &rd[p_ofs]; @@ -149,6 +152,7 @@ int PackedDataContainer::_size(uint32_t p_ofs) const {  }  Variant PackedDataContainer::_key_at_ofs(uint32_t p_ofs, const Variant &p_key, bool &err) const { +	ERR_FAIL_COND_V(p_ofs + 4 > (uint32_t)data.size(), Variant());  	const uint8_t *rd = data.ptr();  	if (!rd) {  		err = true; diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 8da44fd290..87d2b66e5b 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -138,6 +138,7 @@ Error PacketPeer::_get_packet_error() const {  void PacketPeer::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &PacketPeer::_bnd_get_var, DEFVAL(false));  	ClassDB::bind_method(D_METHOD("put_var", "var", "full_objects"), &PacketPeer::put_var, DEFVAL(false)); +  	ClassDB::bind_method(D_METHOD("get_packet"), &PacketPeer::_get_packet);  	ClassDB::bind_method(D_METHOD("put_packet", "buffer"), &PacketPeer::_put_packet);  	ClassDB::bind_method(D_METHOD("get_packet_error"), &PacketPeer::_get_packet_error); @@ -151,6 +152,51 @@ void PacketPeer::_bind_methods() {  /***************/ +int PacketPeerExtension::get_available_packet_count() const { +	int count; +	if (GDVIRTUAL_CALL(_get_available_packet_count, count)) { +		return count; +	} +	WARN_PRINT_ONCE("PacketPeerExtension::_get_available_packet_count is unimplemented!"); +	return -1; +} + +Error PacketPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { +	int err; +	if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { +		return (Error)err; +	} +	WARN_PRINT_ONCE("PacketPeerExtension::_get_packet_native is unimplemented!"); +	return FAILED; +} + +Error PacketPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { +	int err; +	if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { +		return (Error)err; +	} +	WARN_PRINT_ONCE("PacketPeerExtension::_put_packet_native is unimplemented!"); +	return FAILED; +} + +int PacketPeerExtension::get_max_packet_size() const { +	int size; +	if (GDVIRTUAL_CALL(_get_max_packet_size, size)) { +		return size; +	} +	WARN_PRINT_ONCE("PacketPeerExtension::_get_max_packet_size is unimplemented!"); +	return 0; +} + +void PacketPeerExtension::_bind_methods() { +	GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); +	GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); +	GDVIRTUAL_BIND(_get_available_packet_count); +	GDVIRTUAL_BIND(_get_max_packet_size); +} + +/***************/ +  void PacketPeerStream::_bind_methods() {  	ClassDB::bind_method(D_METHOD("set_stream_peer", "peer"), &PacketPeerStream::set_stream_peer);  	ClassDB::bind_method(D_METHOD("get_stream_peer"), &PacketPeerStream::get_stream_peer); diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index 9a345af3d0..bc1f4aaabf 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -35,6 +35,10 @@  #include "core/object/class_db.h"  #include "core/templates/ring_buffer.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" +  class PacketPeer : public RefCounted {  	GDCLASS(PacketPeer, RefCounted); @@ -73,6 +77,25 @@ public:  	~PacketPeer() {}  }; +class PacketPeerExtension : public PacketPeer { +	GDCLASS(PacketPeerExtension, PacketPeer); + +protected: +	static void _bind_methods(); + +public: +	virtual int get_available_packet_count() const override; +	virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet +	virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; +	virtual int get_max_packet_size() const override; + +	/* GDExtension */ +	GDVIRTUAL0RC(int, _get_available_packet_count); +	GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); +	GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int); +	GDVIRTUAL0RC(int, _get_max_packet_size); +}; +  class PacketPeerStream : public PacketPeer {  	GDCLASS(PacketPeerStream, PacketPeer); diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 806a95398f..8d75581342 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -47,13 +47,14 @@ static int _get_pad(int p_alignment, int p_n) {  }  void PCKPacker::_bind_methods() { -	ClassDB::bind_method(D_METHOD("pck_start", "pck_name", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(0), DEFVAL(String()), DEFVAL(false)); +	ClassDB::bind_method(D_METHOD("pck_start", "pck_name", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(32), DEFVAL("0000000000000000000000000000000000000000000000000000000000000000"), DEFVAL(false));  	ClassDB::bind_method(D_METHOD("add_file", "pck_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false));  	ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush, DEFVAL(false));  }  Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &p_key, bool p_encrypt_directory) {  	ERR_FAIL_COND_V_MSG((p_key.is_empty() || !p_key.is_valid_hex_number(false) || p_key.length() != 64), ERR_CANT_CREATE, "Invalid Encryption Key (must be 64 characters long)."); +	ERR_FAIL_COND_V_MSG(p_alignment <= 0, ERR_CANT_CREATE, "Invalid alignment, must be greater then 0.");  	String _key = p_key.to_lower();  	key.resize(32); diff --git a/core/io/pck_packer.h b/core/io/pck_packer.h index 3d2ce8f240..bd8902a01d 100644 --- a/core/io/pck_packer.h +++ b/core/io/pck_packer.h @@ -58,7 +58,7 @@ class PCKPacker : public RefCounted {  	Vector<File> files;  public: -	Error pck_start(const String &p_file, int p_alignment = 0, const String &p_key = String(), bool p_encrypt_directory = false); +	Error pck_start(const String &p_file, int p_alignment = 32, const String &p_key = "0000000000000000000000000000000000000000000000000000000000000000", bool p_encrypt_directory = false);  	Error add_file(const String &p_file, const String &p_src, bool p_encrypt = false);  	Error flush(bool p_verbose = false); diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 87b4d7195d..8da4e936e3 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -52,7 +52,7 @@ void Resource::set_path(const String &p_path, bool p_take_over) {  		return;  	} -	if (path_cache != "") { +	if (!path_cache.is_empty()) {  		ResourceCache::lock.write_lock();  		ResourceCache::resources.erase(path_cache);  		ResourceCache::lock.write_unlock(); @@ -82,7 +82,7 @@ void Resource::set_path(const String &p_path, bool p_take_over) {  	}  	path_cache = p_path; -	if (path_cache != "") { +	if (!path_cache.is_empty()) {  		ResourceCache::lock.write_lock();  		ResourceCache::resources[path_cache] = this;  		ResourceCache::lock.write_unlock(); @@ -136,6 +136,7 @@ String Resource::get_scene_unique_id() const {  void Resource::set_name(const String &p_name) {  	name = p_name; +	emit_changed();  }  String Resource::get_name() const { @@ -382,7 +383,7 @@ bool Resource::is_translation_remapped() const {  #ifdef TOOLS_ENABLED  //helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored  void Resource::set_id_for_path(const String &p_path, const String &p_id) { -	if (p_id == "") { +	if (p_id.is_empty()) {  		ResourceCache::path_cache_lock.write_lock();  		ResourceCache::resource_path_cache[p_path].erase(get_path());  		ResourceCache::path_cache_lock.write_unlock(); @@ -433,7 +434,7 @@ Resource::Resource() :  		remapped_list(this) {}  Resource::~Resource() { -	if (path_cache != "") { +	if (!path_cache.is_empty()) {  		ResourceCache::lock.write_lock();  		ResourceCache::resources.erase(path_cache);  		ResourceCache::lock.write_unlock(); @@ -540,9 +541,9 @@ void ResourceCache::dump(const char *p_file, bool p_short) {  		}  	} -	for (Map<String, int>::Element *E = type_count.front(); E; E = E->next()) { +	for (const KeyValue<String, int> &E : type_count) {  		if (f) { -			f->store_line(E->key() + " count: " + itos(E->get())); +			f->store_line(E.key + " count: " + itos(E.value));  		}  	}  	if (f) { diff --git a/core/io/resource.h b/core/io/resource.h index 9ccc247887..109c0f6611 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -103,6 +103,7 @@ public:  	virtual void set_path(const String &p_path, bool p_take_over = false);  	String get_path() const; +	_FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.find("::") != -1 || path_cache.begins_with("local://"); }  	static String generate_scene_unique_id();  	void set_scene_unique_id(const String &p_id); diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 00d4d093da..bd040f303d 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -335,7 +335,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {  					String exttype = get_unicode_string();  					String path = get_unicode_string(); -					if (path.find("://") == -1 && path.is_rel_path()) { +					if (path.find("://") == -1 && path.is_relative_path()) {  						// path is relative to file being loaded, so convert to a resource path  						path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path));  					} @@ -626,7 +626,7 @@ Error ResourceLoaderBinary::load() {  			path = remaps[path];  		} -		if (path.find("://") == -1 && path.is_rel_path()) { +		if (path.find("://") == -1 && path.is_relative_path()) {  			// path is relative to file being loaded, so convert to a resource path  			path = ProjectSettings::get_singleton()->localize_path(path.get_base_dir().plus_file(external_resources[i].path));  		} @@ -727,7 +727,7 @@ Error ResourceLoaderBinary::load() {  			}  			res = RES(r); -			if (path != String() && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { +			if (!path.is_empty() && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {  				r->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); //if got here because the resource with same path has different type, replace it  			}  			r->set_scene_unique_id(id); @@ -829,7 +829,7 @@ void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dep  			dep = external_resources[i].path;  		} -		if (p_add_types && external_resources[i].type != String()) { +		if (p_add_types && !external_resources[i].type.is_empty()) {  			dep += "::" + external_resources[i].type;  		} @@ -1026,7 +1026,7 @@ RES ResourceFormatLoaderBinary::load(const String &p_path, const String &p_origi  	loader.cache_mode = p_cache_mode;  	loader.use_sub_threads = p_use_sub_threads;  	loader.progress = r_progress; -	String path = p_original_path != "" ? p_original_path : p_path; +	String path = !p_original_path.is_empty() ? p_original_path : p_path;  	loader.local_path = ProjectSettings::get_singleton()->localize_path(path);  	loader.res_path = loader.local_path;  	//loader.set_local_path( Globals::get_singleton()->localize_path(p_path) ); @@ -1045,7 +1045,7 @@ RES ResourceFormatLoaderBinary::load(const String &p_path, const String &p_origi  }  void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { -	if (p_type == "") { +	if (p_type.is_empty()) {  		get_recognized_extensions(p_extensions);  		return;  	} @@ -1572,7 +1572,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia  				return; // don't save it  			} -			if (res->get_path().length() && res->get_path().find("::") == -1) { +			if (!res->is_built_in()) {  				f->store_32(OBJECT_EXTERNAL_RESOURCE_INDEX);  				f->store_32(external_resources[res]);  			} else { @@ -1743,7 +1743,7 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant  				return;  			} -			if (!p_main && (!bundle_resources) && res->get_path().length() && res->get_path().find("::") == -1) { +			if (!p_main && (!bundle_resources) && !res->is_built_in()) {  				if (res->get_path() == path) {  					ERR_PRINT("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded.");  					return; @@ -1960,8 +1960,8 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p  	Vector<RES> save_order;  	save_order.resize(external_resources.size()); -	for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { -		save_order.write[E->get()] = E->key(); +	for (const KeyValue<RES, int> &E : external_resources) { +		save_order.write[E.value] = E.key;  	}  	for (int i = 0; i < save_order.size(); i++) { @@ -1978,8 +1978,8 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p  	Set<String> used_unique_ids;  	for (RES &r : saved_resources) { -		if (r->get_path() == "" || r->get_path().find("::") != -1) { -			if (r->get_scene_unique_id() != "") { +		if (r->is_built_in()) { +			if (!r->get_scene_unique_id().is_empty()) {  				if (used_unique_ids.has(r->get_scene_unique_id())) {  					r->set_scene_unique_id("");  				} else { @@ -1992,8 +1992,8 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p  	Map<RES, int> resource_map;  	int res_index = 0;  	for (RES &r : saved_resources) { -		if (r->get_path() == "" || r->get_path().find("::") != -1) { -			if (r->get_scene_unique_id() == "") { +		if (r->is_built_in()) { +			if (r->get_scene_unique_id().is_empty()) {  				String new_id;  				while (true) { diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index 1e166015b0..fc5c434e37 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -78,8 +78,8 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy  			return err;  		} -		if (assign != String()) { -			if (!path_found && assign.begins_with("path.") && r_path_and_type.path == String()) { +		if (!assign.is_empty()) { +			if (!path_found && assign.begins_with("path.") && r_path_and_type.path.is_empty()) {  				String feature = assign.get_slicec('.', 1);  				if (OS::get_singleton()->has_feature(feature)) {  					r_path_and_type.path = value; @@ -112,7 +112,7 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy  	memdelete(f); -	if (r_path_and_type.path == String() || r_path_and_type.type == String()) { +	if (r_path_and_type.path.is_empty() || r_path_and_type.type.is_empty()) {  		return ERR_FILE_CORRUPT;  	}  	return OK; @@ -158,7 +158,7 @@ void ResourceFormatImporter::get_recognized_extensions(List<String> *p_extension  }  void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { -	if (p_type == "") { +	if (p_type.is_empty()) {  		get_recognized_extensions(p_extensions);  		return;  	} @@ -167,7 +167,7 @@ void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_  	for (int i = 0; i < importers.size(); i++) {  		String res_type = importers[i]->get_resource_type(); -		if (res_type == String()) { +		if (res_type.is_empty()) {  			continue;  		} @@ -246,7 +246,7 @@ int ResourceFormatImporter::get_import_order(const String &p_path) const {  bool ResourceFormatImporter::handles_type(const String &p_type) const {  	for (int i = 0; i < importers.size(); i++) {  		String res_type = importers[i]->get_resource_type(); -		if (res_type == String()) { +		if (res_type.is_empty()) {  			continue;  		}  		if (ClassDB::is_parent_class(res_type, p_type)) { @@ -300,7 +300,7 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat  			return;  		} -		if (assign != String()) { +		if (!assign.is_empty()) {  			if (assign.begins_with("path.")) {  				r_paths->push_back(value);  			} else if (assign == "path") { @@ -418,7 +418,7 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const St  }  String ResourceFormatImporter::get_import_base_path(const String &p_for_file) const { -	return ProjectSettings::IMPORTED_FILES_PATH.plus_file(p_for_file.get_file() + "-" + p_for_file.md5_text()); +	return ProjectSettings::get_singleton()->get_imported_files_path().plus_file(p_for_file.get_file() + "-" + p_for_file.md5_text());  }  bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) const { diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index a1cacbd306..cd583e2533 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -134,8 +134,8 @@ public:  	virtual int get_preset_count() const { return 0; }  	virtual String get_preset_name(int p_idx) const { return String(); } -	virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const = 0; -	virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const = 0; +	virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const = 0; +	virtual bool get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const = 0;  	virtual String get_option_group_file() const { return String(); }  	virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 64237f3b15..f65570bd60 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -52,7 +52,7 @@ bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_  	String extension = p_path.get_extension();  	List<String> extensions; -	if (p_for_type == String()) { +	if (p_for_type.is_empty()) {  		get_recognized_extensions(&extensions);  	} else {  		get_recognized_extensions_for_type(p_for_type, &extensions); @@ -96,7 +96,7 @@ ResourceUID::ID ResourceFormatLoader::get_resource_uid(const String &p_path) con  }  void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { -	if (p_type == "" || handles_type(p_type)) { +	if (p_type.is_empty() || handles_type(p_type)) {  		get_recognized_extensions(p_extensions);  	}  } @@ -156,8 +156,8 @@ void ResourceFormatLoader::get_dependencies(const String &p_path, List<String> *  Error ResourceFormatLoader::rename_dependencies(const String &p_path, const Map<String, String> &p_map) {  	Dictionary deps_dict; -	for (Map<String, String>::Element *E = p_map.front(); E; E = E->next()) { -		deps_dict[E->key()] = E->value(); +	for (KeyValue<String, String> E : p_map) { +		deps_dict[E.key] = E.value;  	}  	int64_t err; @@ -194,7 +194,7 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c  			continue;  		}  		found = true; -		RES res = loader[i]->load(p_path, p_original_path != String() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); +		RES res = loader[i]->load(p_path, !p_original_path.is_empty() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);  		if (res.is_null()) {  			continue;  		} @@ -278,7 +278,7 @@ static String _validate_local_path(const String &p_path) {  	ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(p_path);  	if (uid != ResourceUID::INVALID_ID) {  		return ResourceUID::get_singleton()->get_id_path(uid); -	} else if (p_path.is_rel_path()) { +	} else if (p_path.is_relative_path()) {  		return "res://" + p_path;  	} else {  		return ProjectSettings::get_singleton()->localize_path(p_path); @@ -289,7 +289,7 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String &  	thread_load_mutex->lock(); -	if (p_source_resource != String()) { +	if (!p_source_resource.is_empty()) {  		//must be loading from this resource  		if (!thread_load_tasks.has(p_source_resource)) {  			thread_load_mutex->unlock(); @@ -310,7 +310,7 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String &  	if (thread_load_tasks.has(local_path)) {  		thread_load_tasks[local_path].requests++; -		if (p_source_resource != String()) { +		if (!p_source_resource.is_empty()) {  			thread_load_tasks[p_source_resource].sub_tasks.insert(local_path);  		}  		thread_load_mutex->unlock(); @@ -354,7 +354,7 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String &  			ResourceCache::lock.read_unlock();  		} -		if (p_source_resource != String()) { +		if (!p_source_resource.is_empty()) {  			thread_load_tasks[p_source_resource].sub_tasks.insert(local_path);  		} @@ -574,7 +574,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, Resour  		bool xl_remapped = false;  		String path = _path_remap(local_path, &xl_remapped); -		if (path == "") { +		if (path.is_empty()) {  			ERR_FAIL_V_MSG(RES(), "Remapping '" + local_path + "' failed.");  		} @@ -776,7 +776,7 @@ String ResourceLoader::get_resource_type(const String &p_path) {  	for (int i = 0; i < loader_count; i++) {  		String result = loader[i]->get_resource_type(local_path); -		if (result != "") { +		if (!result.is_empty()) {  			return result;  		}  	} diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index 97d683f415..b7d01712ff 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -29,6 +29,8 @@  /*************************************************************************/  #include "resource_uid.h" + +#include "core/config/project_settings.h"  #include "core/crypto/crypto.h"  #include "core/io/dir_access.h"  #include "core/io/file_access.h" @@ -36,7 +38,9 @@  static constexpr uint32_t char_count = ('z' - 'a');  static constexpr uint32_t base = char_count + ('9' - '0'); -const char *ResourceUID::CACHE_FILE = "res://.godot/uid_cache.bin"; +String ResourceUID::get_cache_file() { +	return ProjectSettings::get_singleton()->get_project_data_path().plus_file("uid_cache.bin"); +}  String ResourceUID::id_to_text(ID p_id) const {  	if (p_id < 0) { @@ -126,8 +130,7 @@ String ResourceUID::get_id_path(ID p_id) const {  	MutexLock l(mutex);  	ERR_FAIL_COND_V(!unique_ids.has(p_id), String());  	const CharString &cs = unique_ids[p_id].cs; -	String s(cs.ptr()); -	return s; +	return String::utf8(cs.ptr());  }  void ResourceUID::remove_id(ID p_id) {  	MutexLock l(mutex); @@ -136,12 +139,13 @@ void ResourceUID::remove_id(ID p_id) {  }  Error ResourceUID::save_to_cache() { -	if (!FileAccess::exists(CACHE_FILE)) { +	String cache_file = get_cache_file(); +	if (!FileAccess::exists(cache_file)) {  		DirAccessRef d = DirAccess::create(DirAccess::ACCESS_RESOURCES); -		d->make_dir_recursive(String(CACHE_FILE).get_base_dir()); //ensure base dir exists +		d->make_dir_recursive(String(cache_file).get_base_dir()); //ensure base dir exists  	} -	FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::WRITE); +	FileAccessRef f = FileAccess::open(cache_file, FileAccess::WRITE);  	if (!f) {  		return ERR_CANT_OPEN;  	} @@ -165,7 +169,7 @@ Error ResourceUID::save_to_cache() {  }  Error ResourceUID::load_from_cache() { -	FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::READ); +	FileAccessRef f = FileAccess::open(get_cache_file(), FileAccess::READ);  	if (!f) {  		return ERR_CANT_OPEN;  	} @@ -207,7 +211,7 @@ Error ResourceUID::update_cache() {  	for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) {  		if (!E.get().saved_to_cache) {  			if (f == nullptr) { -				f = FileAccess::open(CACHE_FILE, FileAccess::READ_WRITE); //append +				f = FileAccess::open(get_cache_file(), FileAccess::READ_WRITE); //append  				if (!f) {  					return ERR_CANT_OPEN;  				} diff --git a/core/io/resource_uid.h b/core/io/resource_uid.h index b12138425a..2f1bfdf243 100644 --- a/core/io/resource_uid.h +++ b/core/io/resource_uid.h @@ -44,7 +44,7 @@ public:  		INVALID_ID = -1  	}; -	static const char *CACHE_FILE; +	static String get_cache_file();  private:  	mutable Ref<Crypto> crypto; diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 27f8d4e88f..8ab025dda1 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -410,6 +410,63 @@ void StreamPeer::_bind_methods() {  //////////////////////////////// +int StreamPeerExtension::get_available_bytes() const { +	int count; +	if (GDVIRTUAL_CALL(_get_available_bytes, count)) { +		return count; +	} +	WARN_PRINT_ONCE("StreamPeerExtension::_get_available_bytes is unimplemented!"); +	return -1; +} + +Error StreamPeerExtension::get_data(uint8_t *r_buffer, int p_bytes) { +	int err; +	int received = 0; +	if (GDVIRTUAL_CALL(_get_data, r_buffer, p_bytes, &received, err)) { +		return (Error)err; +	} +	WARN_PRINT_ONCE("StreamPeerExtension::_get_data is unimplemented!"); +	return FAILED; +} + +Error StreamPeerExtension::get_partial_data(uint8_t *r_buffer, int p_bytes, int &r_received) { +	int err; +	if (GDVIRTUAL_CALL(_get_partial_data, r_buffer, p_bytes, &r_received, err)) { +		return (Error)err; +	} +	WARN_PRINT_ONCE("StreamPeerExtension::_get_partial_data is unimplemented!"); +	return FAILED; +} + +Error StreamPeerExtension::put_data(const uint8_t *p_data, int p_bytes) { +	int err; +	int sent = 0; +	if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &sent, err)) { +		return (Error)err; +	} +	WARN_PRINT_ONCE("StreamPeerExtension::_put_data is unimplemented!"); +	return FAILED; +} + +Error StreamPeerExtension::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { +	int err; +	if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &r_sent, err)) { +		return (Error)err; +	} +	WARN_PRINT_ONCE("StreamPeerExtension::_put_partial_data is unimplemented!"); +	return FAILED; +} + +void StreamPeerExtension::_bind_methods() { +	GDVIRTUAL_BIND(_get_data, "r_buffer", "r_bytes", "r_received"); +	GDVIRTUAL_BIND(_get_partial_data, "r_buffer", "r_bytes", "r_received"); +	GDVIRTUAL_BIND(_put_data, "p_data", "p_bytes", "r_sent"); +	GDVIRTUAL_BIND(_put_partial_data, "p_data", "p_bytes", "r_sent"); +	GDVIRTUAL_BIND(_get_available_bytes); +} + +//////////////////////////////// +  void StreamPeerBuffer::_bind_methods() {  	ClassDB::bind_method(D_METHOD("seek", "position"), &StreamPeerBuffer::seek);  	ClassDB::bind_method(D_METHOD("get_size"), &StreamPeerBuffer::get_size); diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index effc3850af..89432951c5 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -33,6 +33,10 @@  #include "core/object/ref_counted.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" +  class StreamPeer : public RefCounted {  	GDCLASS(StreamPeer, RefCounted);  	OBJ_CATEGORY("Networking"); @@ -58,6 +62,7 @@ public:  	virtual int get_available_bytes() const = 0; +	/* helpers */  	void set_big_endian(bool p_big_endian);  	bool is_big_endian_enabled() const; @@ -92,6 +97,26 @@ public:  	StreamPeer() {}  }; +class StreamPeerExtension : public StreamPeer { +	GDCLASS(StreamPeerExtension, StreamPeer); + +protected: +	static void _bind_methods(); + +public: +	virtual Error put_data(const uint8_t *p_data, int p_bytes) override; +	virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override; +	virtual Error get_data(uint8_t *p_buffer, int p_bytes) override; +	virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override; +	virtual int get_available_bytes() const override; + +	GDVIRTUAL3R(int, _put_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); +	GDVIRTUAL3R(int, _put_partial_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); +	GDVIRTUAL3R(int, _get_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); +	GDVIRTUAL3R(int, _get_partial_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); +	GDVIRTUAL0RC(int, _get_available_bytes); +}; +  class StreamPeerBuffer : public StreamPeer {  	GDCLASS(StreamPeerBuffer, StreamPeer); diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 83d575cee8..cb7d67a726 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -87,7 +87,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {  			// 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 (!skip_this && !msg_id.is_empty()) {  				if (status == STATUS_READING_STRING) {  					translation->add_message(msg_id, msg_str, msg_context);  				} else if (status == STATUS_READING_PLURAL) { @@ -125,7 +125,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {  				ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid', was expecting 'msgstr' while parsing: " + path + ":" + itos(line));  			} -			if (msg_id != "") { +			if (!msg_id.is_empty()) {  				if (!skip_this && !entered_context) {  					if (status == STATUS_READING_STRING) {  						translation->add_message(msg_id, msg_str, msg_context); @@ -137,7 +137,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {  						translation->add_plural_message(msg_id, msgs_plural, msg_context);  					}  				} -			} else if (config == "") { +			} else if (config.is_empty()) {  				config = msg_str;  				// Record plural rule.  				int p_start = config.find("Plural-Forms"); @@ -178,7 +178,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {  			status = STATUS_READING_STRING;  		} -		if (l == "" || l.begins_with("#")) { +		if (l.is_empty() || l.begins_with("#")) {  			if (l.find("fuzzy") != -1) {  				skip_next = true;  			} @@ -236,15 +236,15 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {  	// Add the last set of data from last iteration.  	if (status == STATUS_READING_STRING) { -		if (msg_id != "") { +		if (!msg_id.is_empty()) {  			if (!skip_this) {  				translation->add_message(msg_id, msg_str, msg_context);  			} -		} else if (config == "") { +		} else if (config.is_empty()) {  			config = msg_str;  		}  	} else if (status == STATUS_READING_PLURAL) { -		if (!skip_this && msg_id != "") { +		if (!skip_this && !msg_id.is_empty()) {  			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)); @@ -253,7 +253,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {  		}  	} -	ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + path + "."); +	ERR_FAIL_COND_V_MSG(config.is_empty(), RES(), "No config found in file: " + path + ".");  	Vector<String> configs = config.split("\n");  	for (int i = 0; i < configs.size(); i++) { diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp index fb4c76aa7a..24808cc8d6 100644 --- a/core/io/zip_io.cpp +++ b/core/io/zip_io.cpp @@ -100,7 +100,7 @@ int zipio_testerror(voidpf opaque, voidpf stream) {  }  voidpf zipio_alloc(voidpf opaque, uInt items, uInt size) { -	voidpf ptr = memalloc(items * size); +	voidpf ptr = memalloc((size_t)items * size);  	memset(ptr, 0, items * size);  	return ptr;  } diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index b380860522..bdceae4374 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -47,8 +47,8 @@ int AStar::get_available_point_id() const {  }  void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) { -	ERR_FAIL_COND(p_id < 0); -	ERR_FAIL_COND(p_weight_scale < 1); +	ERR_FAIL_COND_MSG(p_id < 0, vformat("Can't add a point with negative id: %d.", p_id)); +	ERR_FAIL_COND_MSG(p_weight_scale < 1, vformat("Can't add a point with weight scale less than one: %f.", p_weight_scale));  	Point *found_pt;  	bool p_exists = points.lookup(p_id, found_pt); @@ -72,7 +72,7 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {  Vector3 AStar::get_point_position(int p_id) const {  	Point *p;  	bool p_exists = points.lookup(p_id, p); -	ERR_FAIL_COND_V(!p_exists, Vector3()); +	ERR_FAIL_COND_V_MSG(!p_exists, Vector3(), vformat("Can't get point's position. Point with id: %d doesn't exist.", p_id));  	return p->pos;  } @@ -80,7 +80,7 @@ Vector3 AStar::get_point_position(int p_id) const {  void AStar::set_point_position(int p_id, const Vector3 &p_pos) {  	Point *p;  	bool p_exists = points.lookup(p_id, p); -	ERR_FAIL_COND(!p_exists); +	ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set point's position. Point with id: %d doesn't exist.", p_id));  	p->pos = p_pos;  } @@ -88,7 +88,7 @@ void AStar::set_point_position(int p_id, const Vector3 &p_pos) {  real_t AStar::get_point_weight_scale(int p_id) const {  	Point *p;  	bool p_exists = points.lookup(p_id, p); -	ERR_FAIL_COND_V(!p_exists, 0); +	ERR_FAIL_COND_V_MSG(!p_exists, 0, vformat("Can't get point's weight scale. Point with id: %d doesn't exist.", p_id));  	return p->weight_scale;  } @@ -96,8 +96,8 @@ real_t AStar::get_point_weight_scale(int p_id) const {  void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) {  	Point *p;  	bool p_exists = points.lookup(p_id, p); -	ERR_FAIL_COND(!p_exists); -	ERR_FAIL_COND(p_weight_scale < 1); +	ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set point's weight scale. Point with id: %d doesn't exist.", p_id)); +	ERR_FAIL_COND_MSG(p_weight_scale < 1, vformat("Can't set point's weight scale less than one: %f.", p_weight_scale));  	p->weight_scale = p_weight_scale;  } @@ -105,7 +105,7 @@ void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) {  void AStar::remove_point(int p_id) {  	Point *p;  	bool p_exists = points.lookup(p_id, p); -	ERR_FAIL_COND(!p_exists); +	ERR_FAIL_COND_MSG(!p_exists, vformat("Can't remove point. Point with id: %d doesn't exist.", p_id));  	for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {  		Segment s(p_id, (*it.key)); @@ -129,15 +129,15 @@ void AStar::remove_point(int p_id) {  }  void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) { -	ERR_FAIL_COND(p_id == p_with_id); +	ERR_FAIL_COND_MSG(p_id == p_with_id, vformat("Can't connect point with id: %d to itself.", p_id));  	Point *a;  	bool from_exists = points.lookup(p_id, a); -	ERR_FAIL_COND(!from_exists); +	ERR_FAIL_COND_MSG(!from_exists, vformat("Can't connect points. Point with id: %d doesn't exist.", p_id));  	Point *b;  	bool to_exists = points.lookup(p_with_id, b); -	ERR_FAIL_COND(!to_exists); +	ERR_FAIL_COND_MSG(!to_exists, vformat("Can't connect points. Point with id: %d doesn't exist.", p_with_id));  	a->neighbours.set(b->id, b); @@ -169,11 +169,11 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {  void AStar::disconnect_points(int p_id, int p_with_id, bool bidirectional) {  	Point *a;  	bool a_exists = points.lookup(p_id, a); -	ERR_FAIL_COND(!a_exists); +	ERR_FAIL_COND_MSG(!a_exists, vformat("Can't disconnect points. Point with id: %d doesn't exist.", p_id));  	Point *b;  	bool b_exists = points.lookup(p_with_id, b); -	ERR_FAIL_COND(!b_exists); +	ERR_FAIL_COND_MSG(!b_exists, vformat("Can't disconnect points. Point with id: %d doesn't exist.", p_with_id));  	Segment s(p_id, p_with_id);  	int remove_direction = bidirectional ? (int)Segment::BIDIRECTIONAL : s.direction; @@ -210,7 +210,7 @@ bool AStar::has_point(int p_id) const {  	return points.has(p_id);  } -Array AStar::get_points() { +Array AStar::get_point_ids() {  	Array point_list;  	for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) { @@ -223,7 +223,7 @@ Array AStar::get_points() {  Vector<int> AStar::get_point_connections(int p_id) {  	Point *p;  	bool p_exists = points.lookup(p_id, p); -	ERR_FAIL_COND_V(!p_exists, Vector<int>()); +	ERR_FAIL_COND_V_MSG(!p_exists, Vector<int>(), vformat("Can't get point's connections. Point with id: %d doesn't exist.", p_id));  	Vector<int> point_list; @@ -239,7 +239,7 @@ bool AStar::are_points_connected(int p_id, int p_with_id, bool bidirectional) co  	const Set<Segment>::Element *element = segments.find(s);  	return element != nullptr && -		   (bidirectional || (element->get().direction & s.direction) == s.direction); +			(bidirectional || (element->get().direction & s.direction) == s.direction);  }  void AStar::clear() { @@ -260,8 +260,8 @@ int AStar::get_point_capacity() const {  }  void AStar::reserve_space(int p_num_nodes) { -	ERR_FAIL_COND_MSG(p_num_nodes <= 0, "New capacity must be greater than 0, was: " + itos(p_num_nodes) + "."); -	ERR_FAIL_COND_MSG((uint32_t)p_num_nodes < points.get_capacity(), "New capacity must be greater than current capacity: " + itos(points.get_capacity()) + ", new was: " + itos(p_num_nodes) + "."); +	ERR_FAIL_COND_MSG(p_num_nodes <= 0, vformat("New capacity must be greater than 0, new was: %d.", p_num_nodes)); +	ERR_FAIL_COND_MSG((uint32_t)p_num_nodes < points.get_capacity(), vformat("New capacity must be greater than current capacity: %d, new was: %d.", points.get_capacity(), p_num_nodes));  	points.reserve(p_num_nodes);  } @@ -344,7 +344,7 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {  		}  		sorter.pop_heap(0, open_list.size(), open_list.ptrw()); // Remove the current point from the open list -		open_list.remove(open_list.size() - 1); +		open_list.remove_at(open_list.size() - 1);  		p->closed_pass = pass; // Mark the point as closed  		for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) { @@ -389,11 +389,11 @@ real_t AStar::_estimate_cost(int p_from_id, int p_to_id) {  	Point *from_point;  	bool from_exists = points.lookup(p_from_id, from_point); -	ERR_FAIL_COND_V(!from_exists, 0); +	ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_from_id));  	Point *to_point;  	bool to_exists = points.lookup(p_to_id, to_point); -	ERR_FAIL_COND_V(!to_exists, 0); +	ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_to_id));  	return from_point->pos.distance_to(to_point->pos);  } @@ -406,11 +406,11 @@ real_t AStar::_compute_cost(int p_from_id, int p_to_id) {  	Point *from_point;  	bool from_exists = points.lookup(p_from_id, from_point); -	ERR_FAIL_COND_V(!from_exists, 0); +	ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_from_id));  	Point *to_point;  	bool to_exists = points.lookup(p_to_id, to_point); -	ERR_FAIL_COND_V(!to_exists, 0); +	ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_to_id));  	return from_point->pos.distance_to(to_point->pos);  } @@ -418,11 +418,11 @@ real_t AStar::_compute_cost(int p_from_id, int p_to_id) {  Vector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {  	Point *a;  	bool from_exists = points.lookup(p_from_id, a); -	ERR_FAIL_COND_V(!from_exists, Vector<Vector3>()); +	ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id));  	Point *b;  	bool to_exists = points.lookup(p_to_id, b); -	ERR_FAIL_COND_V(!to_exists, Vector<Vector3>()); +	ERR_FAIL_COND_V_MSG(!to_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id));  	if (a == b) {  		Vector<Vector3> ret; @@ -467,11 +467,11 @@ Vector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {  Vector<int> AStar::get_id_path(int p_from_id, int p_to_id) {  	Point *a;  	bool from_exists = points.lookup(p_from_id, a); -	ERR_FAIL_COND_V(!from_exists, Vector<int>()); +	ERR_FAIL_COND_V_MSG(!from_exists, Vector<int>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id));  	Point *b;  	bool to_exists = points.lookup(p_to_id, b); -	ERR_FAIL_COND_V(!to_exists, Vector<int>()); +	ERR_FAIL_COND_V_MSG(!to_exists, Vector<int>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id));  	if (a == b) {  		Vector<int> ret; @@ -516,7 +516,7 @@ Vector<int> AStar::get_id_path(int p_from_id, int p_to_id) {  void AStar::set_point_disabled(int p_id, bool p_disabled) {  	Point *p;  	bool p_exists = points.lookup(p_id, p); -	ERR_FAIL_COND(!p_exists); +	ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set if point is disabled. Point with id: %d doesn't exist.", p_id));  	p->enabled = !p_disabled;  } @@ -524,7 +524,7 @@ void AStar::set_point_disabled(int p_id, bool p_disabled) {  bool AStar::is_point_disabled(int p_id) const {  	Point *p;  	bool p_exists = points.lookup(p_id, p); -	ERR_FAIL_COND_V(!p_exists, false); +	ERR_FAIL_COND_V_MSG(!p_exists, false, vformat("Can't get if point is disabled. Point with id: %d doesn't exist.", p_id));  	return !p->enabled;  } @@ -539,7 +539,7 @@ void AStar::_bind_methods() {  	ClassDB::bind_method(D_METHOD("remove_point", "id"), &AStar::remove_point);  	ClassDB::bind_method(D_METHOD("has_point", "id"), &AStar::has_point);  	ClassDB::bind_method(D_METHOD("get_point_connections", "id"), &AStar::get_point_connections); -	ClassDB::bind_method(D_METHOD("get_points"), &AStar::get_points); +	ClassDB::bind_method(D_METHOD("get_point_ids"), &AStar::get_point_ids);  	ClassDB::bind_method(D_METHOD("set_point_disabled", "id", "disabled"), &AStar::set_point_disabled, DEFVAL(true));  	ClassDB::bind_method(D_METHOD("is_point_disabled", "id"), &AStar::is_point_disabled); @@ -606,8 +606,8 @@ Vector<int> AStar2D::get_point_connections(int p_id) {  	return astar.get_point_connections(p_id);  } -Array AStar2D::get_points() { -	return astar.get_points(); +Array AStar2D::get_point_ids() { +	return astar.get_point_ids();  }  void AStar2D::set_point_disabled(int p_id, bool p_disabled) { @@ -663,11 +663,11 @@ real_t AStar2D::_estimate_cost(int p_from_id, int p_to_id) {  	AStar::Point *from_point;  	bool from_exists = astar.points.lookup(p_from_id, from_point); -	ERR_FAIL_COND_V(!from_exists, 0); +	ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_from_id));  	AStar::Point *to_point;  	bool to_exists = astar.points.lookup(p_to_id, to_point); -	ERR_FAIL_COND_V(!to_exists, 0); +	ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_to_id));  	return from_point->pos.distance_to(to_point->pos);  } @@ -680,11 +680,11 @@ real_t AStar2D::_compute_cost(int p_from_id, int p_to_id) {  	AStar::Point *from_point;  	bool from_exists = astar.points.lookup(p_from_id, from_point); -	ERR_FAIL_COND_V(!from_exists, 0); +	ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_from_id));  	AStar::Point *to_point;  	bool to_exists = astar.points.lookup(p_to_id, to_point); -	ERR_FAIL_COND_V(!to_exists, 0); +	ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_to_id));  	return from_point->pos.distance_to(to_point->pos);  } @@ -692,11 +692,11 @@ real_t AStar2D::_compute_cost(int p_from_id, int p_to_id) {  Vector<Vector2> AStar2D::get_point_path(int p_from_id, int p_to_id) {  	AStar::Point *a;  	bool from_exists = astar.points.lookup(p_from_id, a); -	ERR_FAIL_COND_V(!from_exists, Vector<Vector2>()); +	ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id));  	AStar::Point *b;  	bool to_exists = astar.points.lookup(p_to_id, b); -	ERR_FAIL_COND_V(!to_exists, Vector<Vector2>()); +	ERR_FAIL_COND_V_MSG(!to_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id));  	if (a == b) {  		Vector<Vector2> ret; @@ -741,11 +741,11 @@ Vector<Vector2> AStar2D::get_point_path(int p_from_id, int p_to_id) {  Vector<int> AStar2D::get_id_path(int p_from_id, int p_to_id) {  	AStar::Point *a;  	bool from_exists = astar.points.lookup(p_from_id, a); -	ERR_FAIL_COND_V(!from_exists, Vector<int>()); +	ERR_FAIL_COND_V_MSG(!from_exists, Vector<int>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id));  	AStar::Point *b;  	bool to_exists = astar.points.lookup(p_to_id, b); -	ERR_FAIL_COND_V(!to_exists, Vector<int>()); +	ERR_FAIL_COND_V_MSG(!to_exists, Vector<int>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id));  	if (a == b) {  		Vector<int> ret; @@ -812,7 +812,7 @@ bool AStar2D::_solve(AStar::Point *begin_point, AStar::Point *end_point) {  		}  		sorter.pop_heap(0, open_list.size(), open_list.ptrw()); // Remove the current point from the open list -		open_list.remove(open_list.size() - 1); +		open_list.remove_at(open_list.size() - 1);  		p->closed_pass = astar.pass; // Mark the point as closed  		for (OAHashMap<int, AStar::Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) { @@ -859,7 +859,7 @@ void AStar2D::_bind_methods() {  	ClassDB::bind_method(D_METHOD("remove_point", "id"), &AStar2D::remove_point);  	ClassDB::bind_method(D_METHOD("has_point", "id"), &AStar2D::has_point);  	ClassDB::bind_method(D_METHOD("get_point_connections", "id"), &AStar2D::get_point_connections); -	ClassDB::bind_method(D_METHOD("get_points"), &AStar2D::get_points); +	ClassDB::bind_method(D_METHOD("get_point_ids"), &AStar2D::get_point_ids);  	ClassDB::bind_method(D_METHOD("set_point_disabled", "id", "disabled"), &AStar2D::set_point_disabled, DEFVAL(true));  	ClassDB::bind_method(D_METHOD("is_point_disabled", "id"), &AStar2D::is_point_disabled); diff --git a/core/math/a_star.h b/core/math/a_star.h index 64fa32a325..ef6f22d228 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -138,7 +138,7 @@ public:  	void remove_point(int p_id);  	bool has_point(int p_id) const;  	Vector<int> get_point_connections(int p_id); -	Array get_points(); +	Array get_point_ids();  	void set_point_disabled(int p_id, bool p_disabled = true);  	bool is_point_disabled(int p_id) const; @@ -188,7 +188,7 @@ public:  	void remove_point(int p_id);  	bool has_point(int p_id) const;  	Vector<int> get_point_connections(int p_id); -	Array get_points(); +	Array get_point_ids();  	void set_point_disabled(int p_id, bool p_disabled = true);  	bool is_point_disabled(int p_id) const; diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index 33aa65f15d..83726f46b5 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -33,7 +33,7 @@  #include "core/string/print_string.h"  #include "core/variant/variant.h" -real_t AABB::get_area() const { +real_t AABB::get_volume() const {  	return size.x * size.y * size.z;  } @@ -46,14 +46,19 @@ bool AABB::operator!=(const AABB &p_rval) const {  }  void AABB::merge_with(const AABB &p_aabb) { +#ifdef MATH_CHECKS +	if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) { +		ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); +	} +#endif  	Vector3 beg_1, beg_2;  	Vector3 end_1, end_2;  	Vector3 min, max;  	beg_1 = position;  	beg_2 = p_aabb.position; -	end_1 = Vector3(size.x, size.y, size.z) + beg_1; -	end_2 = Vector3(p_aabb.size.x, p_aabb.size.y, p_aabb.size.z) + beg_2; +	end_1 = size + beg_1; +	end_2 = p_aabb.size + beg_2;  	min.x = (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x;  	min.y = (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y; @@ -72,6 +77,11 @@ bool AABB::is_equal_approx(const AABB &p_aabb) const {  }  AABB AABB::intersection(const AABB &p_aabb) const { +#ifdef MATH_CHECKS +	if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) { +		ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); +	} +#endif  	Vector3 src_min = position;  	Vector3 src_max = position + size;  	Vector3 dst_min = p_aabb.position; @@ -104,6 +114,11 @@ AABB AABB::intersection(const AABB &p_aabb) const {  }  bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *r_clip, Vector3 *r_normal) const { +#ifdef MATH_CHECKS +	if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) { +		ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); +	} +#endif  	Vector3 c1, c2;  	Vector3 end = position + size;  	real_t near = -1e20; @@ -147,6 +162,11 @@ bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *  }  bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector3 *r_clip, Vector3 *r_normal) const { +#ifdef MATH_CHECKS +	if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) { +		ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); +	} +#endif  	real_t min = 0, max = 1;  	int axis = 0;  	real_t sign = 0; diff --git a/core/math/aabb.h b/core/math/aabb.h index e16246902a..81124002e2 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -46,8 +46,8 @@ public:  	Vector3 position;  	Vector3 size; -	real_t get_area() const; /// get area -	_FORCE_INLINE_ bool has_no_area() const { +	real_t get_volume() const; +	_FORCE_INLINE_ bool has_no_volume() const {  		return (size.x <= 0 || size.y <= 0 || size.z <= 0);  	} @@ -118,6 +118,10 @@ public:  		return position + size;  	} +	_FORCE_INLINE_ Vector3 get_center() const { +		return position + (size * 0.5); +	} +  	operator String() const;  	_FORCE_INLINE_ AABB() {} @@ -128,6 +132,11 @@ public:  };  inline bool AABB::intersects(const AABB &p_aabb) const { +#ifdef MATH_CHECKS +	if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) { +		ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); +	} +#endif  	if (position.x >= (p_aabb.position.x + p_aabb.size.x)) {  		return false;  	} @@ -151,6 +160,11 @@ inline bool AABB::intersects(const AABB &p_aabb) const {  }  inline bool AABB::intersects_inclusive(const AABB &p_aabb) const { +#ifdef MATH_CHECKS +	if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) { +		ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); +	} +#endif  	if (position.x > (p_aabb.position.x + p_aabb.size.x)) {  		return false;  	} @@ -174,6 +188,11 @@ inline bool AABB::intersects_inclusive(const AABB &p_aabb) const {  }  inline bool AABB::encloses(const AABB &p_aabb) const { +#ifdef MATH_CHECKS +	if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) { +		ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); +	} +#endif  	Vector3 src_min = position;  	Vector3 src_max = position + size;  	Vector3 dst_min = p_aabb.position; @@ -196,7 +215,7 @@ Vector3 AABB::get_support(const Vector3 &p_normal) const {  				   (p_normal.x > 0) ? half_extents.x : -half_extents.x,  				   (p_normal.y > 0) ? half_extents.y : -half_extents.y,  				   (p_normal.z > 0) ? half_extents.z : -half_extents.z) + -		   ofs; +			ofs;  }  Vector3 AABB::get_endpoint(int p_point) const { @@ -284,6 +303,11 @@ bool AABB::inside_convex_shape(const Plane *p_planes, int p_plane_count) const {  }  bool AABB::has_point(const Vector3 &p_point) const { +#ifdef MATH_CHECKS +	if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) { +		ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); +	} +#endif  	if (p_point.x < position.x) {  		return false;  	} @@ -307,6 +331,11 @@ bool AABB::has_point(const Vector3 &p_point) const {  }  inline void AABB::expand_to(const Vector3 &p_vector) { +#ifdef MATH_CHECKS +	if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) { +		ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); +	} +#endif  	Vector3 begin = position;  	Vector3 end = position + size; @@ -373,6 +402,11 @@ inline real_t AABB::get_shortest_axis_size() const {  }  bool AABB::smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t t0, real_t t1) const { +#ifdef MATH_CHECKS +	if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) { +		ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); +	} +#endif  	real_t divx = 1.0 / p_dir.x;  	real_t divy = 1.0 / p_dir.y;  	real_t divz = 1.0 / p_dir.z; diff --git a/core/math/audio_frame.h b/core/math/audio_frame.h index a5616b8d79..4a11b99fe8 100644 --- a/core/math/audio_frame.h +++ b/core/math/audio_frame.h @@ -124,10 +124,9 @@ struct AudioFrame {  		r = p_frame.r;  	} -	_ALWAYS_INLINE_ AudioFrame &operator=(const AudioFrame &p_frame) { +	_ALWAYS_INLINE_ void operator=(const AudioFrame &p_frame) {  		l = p_frame.l;  		r = p_frame.r; -		return *this;  	}  	_ALWAYS_INLINE_ operator Vector2() const { diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 5c42213e61..566300c716 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -58,8 +58,8 @@ void Basis::invert() {  		cofac(1, 1, 2, 2), cofac(1, 2, 2, 0), cofac(1, 0, 2, 1)  	};  	real_t det = elements[0][0] * co[0] + -				 elements[0][1] * co[1] + -				 elements[0][2] * co[2]; +			elements[0][1] * co[1] + +			elements[0][2] * co[2];  #ifdef MATH_CHECKS  	ERR_FAIL_COND(det == 0);  #endif @@ -207,6 +207,10 @@ Basis Basis::transposed() const {  	return tr;  } +Basis Basis::from_scale(const Vector3 &p_scale) { +	return Basis(p_scale.x, 0, 0, 0, p_scale.y, 0, 0, 0, p_scale.z); +} +  // Multiplies the matrix from left by the scaling matrix: M -> S.M  // See the comment for Basis::rotated for further explanation.  void Basis::scale(const Vector3 &p_scale) { @@ -246,10 +250,7 @@ void Basis::make_scale_uniform() {  }  Basis Basis::scaled_local(const Vector3 &p_scale) const { -	Basis b; -	b.set_diagonal(p_scale); - -	return (*this) * b; +	return (*this) * Basis::from_scale(p_scale);  }  Vector3 Basis::get_scale_abs() const { @@ -260,7 +261,7 @@ Vector3 Basis::get_scale_abs() const {  }  Vector3 Basis::get_scale_local() const { -	real_t det_sign = SGN(determinant()); +	real_t det_sign = SIGN(determinant());  	return det_sign * Vector3(elements[0].length(), elements[1].length(), elements[2].length());  } @@ -286,11 +287,8 @@ Vector3 Basis::get_scale() const {  	// matrix elements.  	//  	// The rotation part of this decomposition is returned by get_rotation* functions. -	real_t det_sign = SGN(determinant()); -	return det_sign * Vector3( -							  Vector3(elements[0][0], elements[1][0], elements[2][0]).length(), -							  Vector3(elements[0][1], elements[1][1], elements[2][1]).length(), -							  Vector3(elements[0][2], elements[1][2], elements[2][2]).length()); +	real_t det_sign = SIGN(determinant()); +	return det_sign * get_scale_abs();  }  // Decomposes a Basis into a rotation-reflection matrix (an element of the group O(3)) and a positive scaling matrix as B = O.S. @@ -353,7 +351,7 @@ void Basis::rotate(const Quaternion &p_quaternion) {  	*this = rotated(p_quaternion);  } -Vector3 Basis::get_rotation_euler() const { +Vector3 Basis::get_euler_normalized(EulerOrder p_order) const {  	// Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S,  	// and returns the Euler angles corresponding to the rotation part, complementing get_scale().  	// See the comment in get_scale() for further information. @@ -364,7 +362,7 @@ Vector3 Basis::get_rotation_euler() const {  		m.scale(Vector3(-1, -1, -1));  	} -	return m.get_euler(); +	return m.get_euler(p_order);  }  Quaternion Basis::get_rotation_quaternion() const { @@ -423,218 +421,203 @@ void Basis::get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) cons  	p_angle = -p_angle;  } -// get_euler_xyz returns a vector containing the Euler angles in the format -// (a1,a2,a3), where a3 is the angle of the first rotation, and a1 is the last -// (following the convention they are commonly defined in the literature). -// -// The current implementation uses XYZ convention (Z is the first rotation), -// so euler.z is the angle of the (first) rotation around Z axis and so on, -// -// And thus, assuming the matrix is a rotation matrix, this function returns -// the angles in the decomposition R = X(a1).Y(a2).Z(a3) where Z(a) rotates -// around the z-axis by a and so on. -Vector3 Basis::get_euler_xyz() const { -	// Euler angles in XYZ convention. -	// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix -	// -	// rot =  cy*cz          -cy*sz           sy -	//        cz*sx*sy+cx*sz  cx*cz-sx*sy*sz -cy*sx -	//       -cx*cz*sy+sx*sz  cz*sx+cx*sy*sz  cx*cy - -	Vector3 euler; -	real_t sy = elements[0][2]; -	if (sy < (1.0 - CMP_EPSILON)) { -		if (sy > -(1.0 - CMP_EPSILON)) { -			// is this a pure Y rotation? -			if (elements[1][0] == 0.0 && elements[0][1] == 0.0 && elements[1][2] == 0 && elements[2][1] == 0 && elements[1][1] == 1) { -				// return the simplest form (human friendlier in editor and scripts) -				euler.x = 0; -				euler.y = atan2(elements[0][2], elements[0][0]); -				euler.z = 0; +Vector3 Basis::get_euler(EulerOrder p_order) const { +	switch (p_order) { +		case EULER_ORDER_XYZ: { +			// Euler angles in XYZ convention. +			// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix +			// +			// rot =  cy*cz          -cy*sz           sy +			//        cz*sx*sy+cx*sz  cx*cz-sx*sy*sz -cy*sx +			//       -cx*cz*sy+sx*sz  cz*sx+cx*sy*sz  cx*cy + +			Vector3 euler; +			real_t sy = elements[0][2]; +			if (sy < (1.0 - CMP_EPSILON)) { +				if (sy > -(1.0 - CMP_EPSILON)) { +					// is this a pure Y rotation? +					if (elements[1][0] == 0.0 && elements[0][1] == 0.0 && elements[1][2] == 0 && elements[2][1] == 0 && elements[1][1] == 1) { +						// return the simplest form (human friendlier in editor and scripts) +						euler.x = 0; +						euler.y = atan2(elements[0][2], elements[0][0]); +						euler.z = 0; +					} else { +						euler.x = Math::atan2(-elements[1][2], elements[2][2]); +						euler.y = Math::asin(sy); +						euler.z = Math::atan2(-elements[0][1], elements[0][0]); +					} +				} else { +					euler.x = Math::atan2(elements[2][1], elements[1][1]); +					euler.y = -Math_PI / 2.0; +					euler.z = 0.0; +				}  			} else { -				euler.x = Math::atan2(-elements[1][2], elements[2][2]); -				euler.y = Math::asin(sy); -				euler.z = Math::atan2(-elements[0][1], elements[0][0]); +				euler.x = Math::atan2(elements[2][1], elements[1][1]); +				euler.y = Math_PI / 2.0; +				euler.z = 0.0; +			} +			return euler; +		} break; +		case EULER_ORDER_XZY: { +			// Euler angles in XZY convention. +			// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix +			// +			// rot =  cz*cy             -sz             cz*sy +			//        sx*sy+cx*cy*sz    cx*cz           cx*sz*sy-cy*sx +			//        cy*sx*sz          cz*sx           cx*cy+sx*sz*sy + +			Vector3 euler; +			real_t sz = elements[0][1]; +			if (sz < (1.0 - CMP_EPSILON)) { +				if (sz > -(1.0 - CMP_EPSILON)) { +					euler.x = Math::atan2(elements[2][1], elements[1][1]); +					euler.y = Math::atan2(elements[0][2], elements[0][0]); +					euler.z = Math::asin(-sz); +				} else { +					// It's -1 +					euler.x = -Math::atan2(elements[1][2], elements[2][2]); +					euler.y = 0.0; +					euler.z = Math_PI / 2.0; +				} +			} else { +				// It's 1 +				euler.x = -Math::atan2(elements[1][2], elements[2][2]); +				euler.y = 0.0; +				euler.z = -Math_PI / 2.0; +			} +			return euler; +		} break; +		case EULER_ORDER_YXZ: { +			// Euler angles in YXZ convention. +			// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix +			// +			// rot =  cy*cz+sy*sx*sz    cz*sy*sx-cy*sz        cx*sy +			//        cx*sz             cx*cz                 -sx +			//        cy*sx*sz-cz*sy    cy*cz*sx+sy*sz        cy*cx + +			Vector3 euler; + +			real_t m12 = elements[1][2]; + +			if (m12 < (1 - CMP_EPSILON)) { +				if (m12 > -(1 - CMP_EPSILON)) { +					// is this a pure X rotation? +					if (elements[1][0] == 0 && elements[0][1] == 0 && elements[0][2] == 0 && elements[2][0] == 0 && elements[0][0] == 1) { +						// return the simplest form (human friendlier in editor and scripts) +						euler.x = atan2(-m12, elements[1][1]); +						euler.y = 0; +						euler.z = 0; +					} else { +						euler.x = asin(-m12); +						euler.y = atan2(elements[0][2], elements[2][2]); +						euler.z = atan2(elements[1][0], elements[1][1]); +					} +				} else { // m12 == -1 +					euler.x = Math_PI * 0.5; +					euler.y = atan2(elements[0][1], elements[0][0]); +					euler.z = 0; +				} +			} else { // m12 == 1 +				euler.x = -Math_PI * 0.5; +				euler.y = -atan2(elements[0][1], elements[0][0]); +				euler.z = 0;  			} -		} else { -			euler.x = Math::atan2(elements[2][1], elements[1][1]); -			euler.y = -Math_PI / 2.0; -			euler.z = 0.0; -		} -	} else { -		euler.x = Math::atan2(elements[2][1], elements[1][1]); -		euler.y = Math_PI / 2.0; -		euler.z = 0.0; -	} -	return euler; -} - -// set_euler_xyz expects a vector containing the Euler angles in the format -// (ax,ay,az), where ax is the angle of rotation around x axis, -// and similar for other axes. -// The current implementation uses XYZ convention (Z is the first rotation). -void Basis::set_euler_xyz(const Vector3 &p_euler) { -	real_t c, s; - -	c = Math::cos(p_euler.x); -	s = Math::sin(p_euler.x); -	Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); - -	c = Math::cos(p_euler.y); -	s = Math::sin(p_euler.y); -	Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); - -	c = Math::cos(p_euler.z); -	s = Math::sin(p_euler.z); -	Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); - -	//optimizer will optimize away all this anyway -	*this = xmat * (ymat * zmat); -} - -Vector3 Basis::get_euler_xzy() const { -	// Euler angles in XZY convention. -	// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix -	// -	// rot =  cz*cy             -sz             cz*sy -	//        sx*sy+cx*cy*sz    cx*cz           cx*sz*sy-cy*sx -	//        cy*sx*sz          cz*sx           cx*cy+sx*sz*sy - -	Vector3 euler; -	real_t sz = elements[0][1]; -	if (sz < (1.0 - CMP_EPSILON)) { -		if (sz > -(1.0 - CMP_EPSILON)) { -			euler.x = Math::atan2(elements[2][1], elements[1][1]); -			euler.y = Math::atan2(elements[0][2], elements[0][0]); -			euler.z = Math::asin(-sz); -		} else { -			// It's -1 -			euler.x = -Math::atan2(elements[1][2], elements[2][2]); -			euler.y = 0.0; -			euler.z = Math_PI / 2.0; -		} -	} else { -		// It's 1 -		euler.x = -Math::atan2(elements[1][2], elements[2][2]); -		euler.y = 0.0; -		euler.z = -Math_PI / 2.0; -	} -	return euler; -} - -void Basis::set_euler_xzy(const Vector3 &p_euler) { -	real_t c, s; - -	c = Math::cos(p_euler.x); -	s = Math::sin(p_euler.x); -	Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); - -	c = Math::cos(p_euler.y); -	s = Math::sin(p_euler.y); -	Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); - -	c = Math::cos(p_euler.z); -	s = Math::sin(p_euler.z); -	Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); - -	*this = xmat * zmat * ymat; -} - -Vector3 Basis::get_euler_yzx() const { -	// Euler angles in YZX convention. -	// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix -	// -	// rot =  cy*cz             sy*sx-cy*cx*sz     cx*sy+cy*sz*sx -	//        sz                cz*cx              -cz*sx -	//        -cz*sy            cy*sx+cx*sy*sz     cy*cx-sy*sz*sx - -	Vector3 euler; -	real_t sz = elements[1][0]; -	if (sz < (1.0 - CMP_EPSILON)) { -		if (sz > -(1.0 - CMP_EPSILON)) { -			euler.x = Math::atan2(-elements[1][2], elements[1][1]); -			euler.y = Math::atan2(-elements[2][0], elements[0][0]); -			euler.z = Math::asin(sz); -		} else { -			// It's -1 -			euler.x = Math::atan2(elements[2][1], elements[2][2]); -			euler.y = 0.0; -			euler.z = -Math_PI / 2.0; -		} -	} else { -		// It's 1 -		euler.x = Math::atan2(elements[2][1], elements[2][2]); -		euler.y = 0.0; -		euler.z = Math_PI / 2.0; -	} -	return euler; -} - -void Basis::set_euler_yzx(const Vector3 &p_euler) { -	real_t c, s; - -	c = Math::cos(p_euler.x); -	s = Math::sin(p_euler.x); -	Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); - -	c = Math::cos(p_euler.y); -	s = Math::sin(p_euler.y); -	Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); - -	c = Math::cos(p_euler.z); -	s = Math::sin(p_euler.z); -	Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); - -	*this = ymat * zmat * xmat; -} - -// get_euler_yxz returns a vector containing the Euler angles in the YXZ convention, -// as in first-Z, then-X, last-Y. The angles for X, Y, and Z rotations are returned -// as the x, y, and z components of a Vector3 respectively. -Vector3 Basis::get_euler_yxz() const { -	// Euler angles in YXZ convention. -	// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix -	// -	// rot =  cy*cz+sy*sx*sz    cz*sy*sx-cy*sz        cx*sy -	//        cx*sz             cx*cz                 -sx -	//        cy*sx*sz-cz*sy    cy*cz*sx+sy*sz        cy*cx - -	Vector3 euler; - -	real_t m12 = elements[1][2]; -	if (m12 < (1 - CMP_EPSILON)) { -		if (m12 > -(1 - CMP_EPSILON)) { -			// is this a pure X rotation? -			if (elements[1][0] == 0 && elements[0][1] == 0 && elements[0][2] == 0 && elements[2][0] == 0 && elements[0][0] == 1) { -				// return the simplest form (human friendlier in editor and scripts) -				euler.x = atan2(-m12, elements[1][1]); -				euler.y = 0; +			return euler; +		} break; +		case EULER_ORDER_YZX: { +			// Euler angles in YZX convention. +			// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix +			// +			// rot =  cy*cz             sy*sx-cy*cx*sz     cx*sy+cy*sz*sx +			//        sz                cz*cx              -cz*sx +			//        -cz*sy            cy*sx+cx*sy*sz     cy*cx-sy*sz*sx + +			Vector3 euler; +			real_t sz = elements[1][0]; +			if (sz < (1.0 - CMP_EPSILON)) { +				if (sz > -(1.0 - CMP_EPSILON)) { +					euler.x = Math::atan2(-elements[1][2], elements[1][1]); +					euler.y = Math::atan2(-elements[2][0], elements[0][0]); +					euler.z = Math::asin(sz); +				} else { +					// It's -1 +					euler.x = Math::atan2(elements[2][1], elements[2][2]); +					euler.y = 0.0; +					euler.z = -Math_PI / 2.0; +				} +			} else { +				// It's 1 +				euler.x = Math::atan2(elements[2][1], elements[2][2]); +				euler.y = 0.0; +				euler.z = Math_PI / 2.0; +			} +			return euler; +		} break; +		case EULER_ORDER_ZXY: { +			// Euler angles in ZXY convention. +			// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix +			// +			// rot =  cz*cy-sz*sx*sy    -cx*sz                cz*sy+cy*sz*sx +			//        cy*sz+cz*sx*sy    cz*cx                 sz*sy-cz*cy*sx +			//        -cx*sy            sx                    cx*cy +			Vector3 euler; +			real_t sx = elements[2][1]; +			if (sx < (1.0 - CMP_EPSILON)) { +				if (sx > -(1.0 - CMP_EPSILON)) { +					euler.x = Math::asin(sx); +					euler.y = Math::atan2(-elements[2][0], elements[2][2]); +					euler.z = Math::atan2(-elements[0][1], elements[1][1]); +				} else { +					// It's -1 +					euler.x = -Math_PI / 2.0; +					euler.y = Math::atan2(elements[0][2], elements[0][0]); +					euler.z = 0; +				} +			} else { +				// It's 1 +				euler.x = Math_PI / 2.0; +				euler.y = Math::atan2(elements[0][2], elements[0][0]);  				euler.z = 0; +			} +			return euler; +		} break; +		case EULER_ORDER_ZYX: { +			// Euler angles in ZYX convention. +			// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix +			// +			// rot =  cz*cy             cz*sy*sx-cx*sz        sz*sx+cz*cx*cy +			//        cy*sz             cz*cx+sz*sy*sx        cx*sz*sy-cz*sx +			//        -sy               cy*sx                 cy*cx +			Vector3 euler; +			real_t sy = elements[2][0]; +			if (sy < (1.0 - CMP_EPSILON)) { +				if (sy > -(1.0 - CMP_EPSILON)) { +					euler.x = Math::atan2(elements[2][1], elements[2][2]); +					euler.y = Math::asin(-sy); +					euler.z = Math::atan2(elements[1][0], elements[0][0]); +				} else { +					// It's -1 +					euler.x = 0; +					euler.y = Math_PI / 2.0; +					euler.z = -Math::atan2(elements[0][1], elements[1][1]); +				}  			} else { -				euler.x = asin(-m12); -				euler.y = atan2(elements[0][2], elements[2][2]); -				euler.z = atan2(elements[1][0], elements[1][1]); +				// It's 1 +				euler.x = 0; +				euler.y = -Math_PI / 2.0; +				euler.z = -Math::atan2(elements[0][1], elements[1][1]);  			} -		} else { // m12 == -1 -			euler.x = Math_PI * 0.5; -			euler.y = atan2(elements[0][1], elements[0][0]); -			euler.z = 0; +			return euler; +		} break; +		default: { +			ERR_FAIL_V_MSG(Vector3(), "Invalid parameter for get_euler(order)");  		} -	} else { // m12 == 1 -		euler.x = -Math_PI * 0.5; -		euler.y = -atan2(elements[0][1], elements[0][0]); -		euler.z = 0;  	} - -	return euler; +	return Vector3();  } -// set_euler_yxz expects a vector containing the Euler angles in the format -// (ax,ay,az), where ax is the angle of rotation around x axis, -// and similar for other axes. -// The current implementation uses YXZ convention (Z is the first rotation). -void Basis::set_euler_yxz(const Vector3 &p_euler) { +void Basis::set_euler(const Vector3 &p_euler, EulerOrder p_order) {  	real_t c, s;  	c = Math::cos(p_euler.x); @@ -649,102 +632,29 @@ void Basis::set_euler_yxz(const Vector3 &p_euler) {  	s = Math::sin(p_euler.z);  	Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); -	//optimizer will optimize away all this anyway -	*this = ymat * xmat * zmat; -} - -Vector3 Basis::get_euler_zxy() const { -	// Euler angles in ZXY convention. -	// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix -	// -	// rot =  cz*cy-sz*sx*sy    -cx*sz                cz*sy+cy*sz*sx -	//        cy*sz+cz*sx*sy    cz*cx                 sz*sy-cz*cy*sx -	//        -cx*sy            sx                    cx*cy -	Vector3 euler; -	real_t sx = elements[2][1]; -	if (sx < (1.0 - CMP_EPSILON)) { -		if (sx > -(1.0 - CMP_EPSILON)) { -			euler.x = Math::asin(sx); -			euler.y = Math::atan2(-elements[2][0], elements[2][2]); -			euler.z = Math::atan2(-elements[0][1], elements[1][1]); -		} else { -			// It's -1 -			euler.x = -Math_PI / 2.0; -			euler.y = Math::atan2(elements[0][2], elements[0][0]); -			euler.z = 0; +	switch (p_order) { +		case EULER_ORDER_XYZ: { +			*this = xmat * (ymat * zmat); +		} break; +		case EULER_ORDER_XZY: { +			*this = xmat * zmat * ymat; +		} break; +		case EULER_ORDER_YXZ: { +			*this = ymat * xmat * zmat; +		} break; +		case EULER_ORDER_YZX: { +			*this = ymat * zmat * xmat; +		} break; +		case EULER_ORDER_ZXY: { +			*this = zmat * xmat * ymat; +		} break; +		case EULER_ORDER_ZYX: { +			*this = zmat * ymat * xmat; +		} break; +		default: { +			ERR_FAIL_MSG("Invalid order parameter for set_euler(vec3,order)");  		} -	} else { -		// It's 1 -		euler.x = Math_PI / 2.0; -		euler.y = Math::atan2(elements[0][2], elements[0][0]); -		euler.z = 0;  	} -	return euler; -} - -void Basis::set_euler_zxy(const Vector3 &p_euler) { -	real_t c, s; - -	c = Math::cos(p_euler.x); -	s = Math::sin(p_euler.x); -	Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); - -	c = Math::cos(p_euler.y); -	s = Math::sin(p_euler.y); -	Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); - -	c = Math::cos(p_euler.z); -	s = Math::sin(p_euler.z); -	Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); - -	*this = zmat * xmat * ymat; -} - -Vector3 Basis::get_euler_zyx() const { -	// Euler angles in ZYX convention. -	// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix -	// -	// rot =  cz*cy             cz*sy*sx-cx*sz        sz*sx+cz*cx*cy -	//        cy*sz             cz*cx+sz*sy*sx        cx*sz*sy-cz*sx -	//        -sy               cy*sx                 cy*cx -	Vector3 euler; -	real_t sy = elements[2][0]; -	if (sy < (1.0 - CMP_EPSILON)) { -		if (sy > -(1.0 - CMP_EPSILON)) { -			euler.x = Math::atan2(elements[2][1], elements[2][2]); -			euler.y = Math::asin(-sy); -			euler.z = Math::atan2(elements[1][0], elements[0][0]); -		} else { -			// It's -1 -			euler.x = 0; -			euler.y = Math_PI / 2.0; -			euler.z = -Math::atan2(elements[0][1], elements[1][1]); -		} -	} else { -		// It's 1 -		euler.x = 0; -		euler.y = -Math_PI / 2.0; -		euler.z = -Math::atan2(elements[0][1], elements[1][1]); -	} -	return euler; -} - -void Basis::set_euler_zyx(const Vector3 &p_euler) { -	real_t c, s; - -	c = Math::cos(p_euler.x); -	s = Math::sin(p_euler.x); -	Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); - -	c = Math::cos(p_euler.y); -	s = Math::sin(p_euler.y); -	Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); - -	c = Math::cos(p_euler.z); -	s = Math::sin(p_euler.z); -	Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); - -	*this = zmat * ymat * xmat;  }  bool Basis::is_equal_approx(const Basis &p_basis) const { @@ -769,13 +679,13 @@ bool Basis::operator!=(const Basis &p_matrix) const {  Basis::operator String() const {  	return "[X: " + get_axis(0).operator String() + -		   ", Y: " + get_axis(1).operator String() + -		   ", Z: " + get_axis(2).operator String() + "]"; +			", Y: " + get_axis(1).operator String() + +			", Z: " + get_axis(2).operator String() + "]";  }  Quaternion Basis::get_quaternion() const {  #ifdef MATH_CHECKS -	ERR_FAIL_COND_V_MSG(!is_rotation(), Quaternion(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quaternion() or call orthonormalized() instead."); +	ERR_FAIL_COND_V_MSG(!is_rotation(), Quaternion(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quaternion() or call orthonormalized() if the Basis contains linearly independent vectors.");  #endif  	/* Allow getting a quaternion from an unnormalized transform */  	Basis m = *this; @@ -791,9 +701,9 @@ Quaternion Basis::get_quaternion() const {  		temp[1] = ((m.elements[0][2] - m.elements[2][0]) * s);  		temp[2] = ((m.elements[1][0] - m.elements[0][1]) * s);  	} else { -		int i = m.elements[0][0] < m.elements[1][1] ? -						  (m.elements[1][1] < m.elements[2][2] ? 2 : 1) : -						  (m.elements[0][0] < m.elements[2][2] ? 2 : 0); +		int i = m.elements[0][0] < m.elements[1][1] +				? (m.elements[1][1] < m.elements[2][2] ? 2 : 1) +				: (m.elements[0][0] < m.elements[2][2] ? 2 : 0);  		int j = (i + 1) % 3;  		int k = (i + 2) % 3; @@ -991,21 +901,23 @@ void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_phi) {  }  void Basis::set_axis_angle_scale(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { -	set_diagonal(p_scale); +	_set_diagonal(p_scale);  	rotate(p_axis, p_phi);  }  void Basis::set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale) { -	set_diagonal(p_scale); +	_set_diagonal(p_scale);  	rotate(p_euler);  }  void Basis::set_quaternion_scale(const Quaternion &p_quaternion, const Vector3 &p_scale) { -	set_diagonal(p_scale); +	_set_diagonal(p_scale);  	rotate(p_quaternion);  } -void Basis::set_diagonal(const Vector3 &p_diag) { +// This also sets the non-diagonal elements to 0, which is misleading from the +// name, so we want this method to be private. Use `from_scale` externally. +void Basis::_set_diagonal(const Vector3 &p_diag) {  	elements[0][0] = p_diag.x;  	elements[0][1] = 0;  	elements[0][2] = 0; diff --git a/core/math/basis.h b/core/math/basis.h index 9d8ed16e29..e2fdb95685 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -35,6 +35,9 @@  #include "core/math/vector3.h"  class Basis { +private: +	void _set_diagonal(const Vector3 &p_diag); +  public:  	Vector3 elements[3] = {  		Vector3(1, 0, 0), @@ -82,40 +85,35 @@ public:  	void rotate(const Quaternion &p_quaternion);  	Basis rotated(const Quaternion &p_quaternion) const; -	Vector3 get_rotation_euler() const; +	enum EulerOrder { +		EULER_ORDER_XYZ, +		EULER_ORDER_XZY, +		EULER_ORDER_YXZ, +		EULER_ORDER_YZX, +		EULER_ORDER_ZXY, +		EULER_ORDER_ZYX +	}; + +	Vector3 get_euler_normalized(EulerOrder p_order = EULER_ORDER_YXZ) const;  	void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const;  	void get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const;  	Quaternion get_rotation_quaternion() const; -	Vector3 get_rotation() const { return get_rotation_euler(); };  	void rotate_to_align(Vector3 p_start_direction, Vector3 p_end_direction);  	Vector3 rotref_posscale_decomposition(Basis &rotref) const; -	Vector3 get_euler_xyz() const; -	void set_euler_xyz(const Vector3 &p_euler); - -	Vector3 get_euler_xzy() const; -	void set_euler_xzy(const Vector3 &p_euler); - -	Vector3 get_euler_yzx() const; -	void set_euler_yzx(const Vector3 &p_euler); - -	Vector3 get_euler_yxz() const; -	void set_euler_yxz(const Vector3 &p_euler); - -	Vector3 get_euler_zxy() const; -	void set_euler_zxy(const Vector3 &p_euler); - -	Vector3 get_euler_zyx() const; -	void set_euler_zyx(const Vector3 &p_euler); +	Vector3 get_euler(EulerOrder p_order = EULER_ORDER_YXZ) const; +	void set_euler(const Vector3 &p_euler, EulerOrder p_order = EULER_ORDER_YXZ); +	static Basis from_euler(const Vector3 &p_euler, EulerOrder p_order = EULER_ORDER_YXZ) { +		Basis b; +		b.set_euler(p_euler, p_order); +		return b; +	}  	Quaternion get_quaternion() const;  	void set_quaternion(const Quaternion &p_quaternion); -	Vector3 get_euler() const { return get_euler_yxz(); } -	void set_euler(const Vector3 &p_euler) { set_euler_yxz(p_euler); } -  	void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const;  	void set_axis_angle(const Vector3 &p_axis, real_t p_phi); @@ -166,8 +164,6 @@ public:  	int get_orthogonal_index() const;  	void set_orthogonal_index(int p_index); -	void set_diagonal(const Vector3 &p_diag); -  	bool is_orthogonal() const;  	bool is_diagonal() const;  	bool is_rotation() const; @@ -249,11 +245,9 @@ public:  	Basis(const Quaternion &p_quaternion) { set_quaternion(p_quaternion); };  	Basis(const Quaternion &p_quaternion, const Vector3 &p_scale) { set_quaternion_scale(p_quaternion, p_scale); } -	Basis(const Vector3 &p_euler) { set_euler(p_euler); } -	Basis(const Vector3 &p_euler, const Vector3 &p_scale) { set_euler_scale(p_euler, p_scale); } -  	Basis(const Vector3 &p_axis, real_t p_phi) { set_axis_angle(p_axis, p_phi); }  	Basis(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_phi, p_scale); } +	static Basis from_scale(const Vector3 &p_scale);  	_FORCE_INLINE_ Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) {  		elements[0] = row0; @@ -330,7 +324,7 @@ Vector3 Basis::xform_inv(const Vector3 &p_vector) const {  real_t Basis::determinant() const {  	return elements[0][0] * (elements[1][1] * elements[2][2] - elements[2][1] * elements[1][2]) - -		   elements[1][0] * (elements[0][1] * elements[2][2] - elements[2][1] * elements[0][2]) + -		   elements[2][0] * (elements[0][1] * elements[1][2] - elements[1][1] * elements[0][2]); +			elements[1][0] * (elements[0][1] * elements[2][2] - elements[2][1] * elements[0][2]) + +			elements[2][0] * (elements[0][1] * elements[1][2] - elements[1][1] * elements[0][2]);  }  #endif // BASIS_H diff --git a/core/math/bvh.h b/core/math/bvh.h index cefbc9b0db..c1eff02178 100644 --- a/core/math/bvh.h +++ b/core/math/bvh.h @@ -200,7 +200,7 @@ public:  	// use in conjunction with activate if you have deferred the collision check, and  	// set pairable has never been called. -	// (deferred collision checks are a workaround for visual server for historical reasons) +	// (deferred collision checks are a workaround for rendering server for historical reasons)  	void force_collision_check(BVHHandle p_handle) {  		if (USE_PAIRS) {  			// the aabb should already be up to date in the BVH @@ -654,7 +654,7 @@ private:  		// remove from changed items (not very efficient yet)  		for (int n = 0; n < (int)changed_items.size(); n++) {  			if (changed_items[n] == p_handle) { -				changed_items.remove_unordered(n); +				changed_items.remove_at_unordered(n);  				// because we are using an unordered remove,  				// the last changed item will now be at spot 'n', diff --git a/core/math/bvh_cull.inc b/core/math/bvh_cull.inc index cba8ea6cb3..d7edc8a884 100644 --- a/core/math/bvh_cull.inc +++ b/core/math/bvh_cull.inc @@ -14,7 +14,7 @@ struct CullParams {  	uint32_t pairable_type;  	// optional components for different tests -	Vector3 point; +	Point point;  	BVHABB_CLASS abb;  	typename BVHABB_CLASS::ConvexHull hull;  	typename BVHABB_CLASS::Segment segment; diff --git a/core/math/bvh_debug.inc b/core/math/bvh_debug.inc index a97304334c..55db794ee3 100644 --- a/core/math/bvh_debug.inc +++ b/core/math/bvh_debug.inc @@ -6,24 +6,21 @@ void _debug_recursive_print_tree(int p_tree_id) const {  }  String _debug_aabb_to_string(const BVHABB_CLASS &aabb) const { -	String sz = "("; -	sz += itos(aabb.min.x); -	sz += " ~ "; -	sz += itos(-aabb.neg_max.x); -	sz += ") ("; +	Point size = aabb.calculate_size(); -	sz += itos(aabb.min.y); -	sz += " ~ "; -	sz += itos(-aabb.neg_max.y); -	sz += ") ("; +	String sz; +	float vol = 0.0; -	sz += itos(aabb.min.z); -	sz += " ~ "; -	sz += itos(-aabb.neg_max.z); -	sz += ") "; +	for (int i = 0; i < Point::AXES_COUNT; ++i) { +		sz += "("; +		sz += itos(aabb.min[i]); +		sz += " ~ "; +		sz += itos(-aabb.neg_max[i]); +		sz += ") "; + +		vol += size[i]; +	} -	Vector3 size = aabb.calculate_size(); -	float vol = size.x * size.y * size.z;  	sz += "vol " + itos(vol);  	return sz; diff --git a/core/math/bvh_logic.inc b/core/math/bvh_logic.inc index afab08f151..c65002a9fd 100644 --- a/core/math/bvh_logic.inc +++ b/core/math/bvh_logic.inc @@ -42,24 +42,24 @@ BVHABB_CLASS _logic_abb_merge(const BVHABB_CLASS &a, const BVHABB_CLASS &b) {  //--------------------------------------------------------------------------------------------------  /** -@file	q3DynamicAABBTree.h -@author	Randy Gaul -@date	10/10/2014 -	Copyright (c) 2014 Randy Gaul http://www.randygaul.net -	This software is provided 'as-is', without any express or implied -	warranty. In no event will the authors be held liable for any damages -	arising from the use of this software. -	Permission is granted to anyone to use this software for any purpose, -	including commercial applications, and to alter it and redistribute it -	freely, subject to the following restrictions: -	  1. The origin of this software must not be misrepresented; you must not -	     claim that you wrote the original software. If you use this software -	     in a product, an acknowledgment in the product documentation would be -	     appreciated but is not required. -	  2. Altered source versions must be plainly marked as such, and must not -	     be misrepresented as being the original software. -	  3. This notice may not be removed or altered from any source distribution. -*/ + * @file	q3DynamicAABBTree.h + * @author	Randy Gaul + * @date	10/10/2014 + *  Copyright (c) 2014 Randy Gaul http://www.randygaul.net + *  This software is provided 'as-is', without any express or implied + *  warranty. In no event will the authors be held liable for any damages + *  arising from the use of this software. + *  Permission is granted to anyone to use this software for any purpose, + *  including commercial applications, and to alter it and redistribute it + *  freely, subject to the following restrictions: + *    1. The origin of this software must not be misrepresented; you must not + *       claim that you wrote the original software. If you use this software + *       in a product, an acknowledgment in the product documentation would be + *       appreciated but is not required. + *    2. Altered source versions must be plainly marked as such, and must not + *       be misrepresented as being the original software. + *    3. This notice may not be removed or altered from any source distribution. + */  //--------------------------------------------------------------------------------------------------  // This function is based on the 'Balance' function from Randy Gaul's qu3e @@ -67,7 +67,7 @@ BVHABB_CLASS _logic_abb_merge(const BVHABB_CLASS &a, const BVHABB_CLASS &b) {  // It is MODIFIED from qu3e version.  // This is the only function used (and _logic_abb_merge helper function).  int32_t _logic_balance(int32_t iA, uint32_t p_tree_id) { -	//	return iA; // uncomment this to bypass balance +	//return iA; // uncomment this to bypass balance  	TNode *A = &_nodes[iA]; @@ -75,12 +75,12 @@ int32_t _logic_balance(int32_t iA, uint32_t p_tree_id) {  		return iA;  	} -	/*      A -	      /   \ -	     B     C -	    / \   / \ -	   D   E F   G -	*/ +	/*       A +	 *     /   \ +	 *    B     C +	 *   / \   / \ +	 *  D   E F   G +	 */  	CRASH_COND(A->num_children != 2);  	int32_t iB = A->children[0]; diff --git a/core/math/bvh_pair.inc b/core/math/bvh_pair.inc index 839db59a3a..a12acec2b6 100644 --- a/core/math/bvh_pair.inc +++ b/core/math/bvh_pair.inc @@ -51,7 +51,7 @@ struct ItemPairs {  		for (int n = 0; n < num_pairs; n++) {  			if (extended_pairs[n].handle == h) {  				userdata = extended_pairs[n].userdata; -				extended_pairs.remove_unordered(n); +				extended_pairs.remove_at_unordered(n);  				num_pairs--;  				break;  			} diff --git a/core/math/bvh_split.inc b/core/math/bvh_split.inc index 3fcc4c7b10..f19ee8a7da 100644 --- a/core/math/bvh_split.inc +++ b/core/math/bvh_split.inc @@ -28,11 +28,15 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u  	Point centre = full_bound.calculate_centre();  	Point size = full_bound.calculate_size(); -	int order[3]; +	int order[Point::AXIS_COUNT]; -	order[0] = size.min_axis(); -	order[2] = size.max_axis(); -	order[1] = 3 - (order[0] + order[2]); +	order[0] = size.min_axis_index(); +	order[Point::AXIS_COUNT - 1] = size.max_axis_index(); + +	static_assert(Point::AXIS_COUNT <= 3); +	if (Point::AXIS_COUNT == 3) { +		order[1] = 3 - (order[0] + order[2]); +	}  	// simplest case, split on the longest axis  	int split_axis = order[0]; @@ -54,7 +58,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u  	// detect when split on longest axis failed  	int min_threshold = MAX_ITEMS / 4; -	int min_group_size[3]; +	int min_group_size[Point::AXIS_COUNT];  	min_group_size[0] = MIN(num_a, num_b);  	if (min_group_size[0] < min_threshold) {  		// slow but sure .. first move everything back into a @@ -64,7 +68,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u  		num_b = 0;  		// now calculate the best split -		for (int axis = 1; axis < 3; axis++) { +		for (int axis = 1; axis < Point::AXIS_COUNT; axis++) {  			split_axis = order[axis];  			int count = 0; @@ -82,7 +86,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u  		// best axis  		int best_axis = 0;  		int best_min = min_group_size[0]; -		for (int axis = 1; axis < 3; axis++) { +		for (int axis = 1; axis < Point::AXIS_COUNT; axis++) {  			if (min_group_size[axis] > best_min) {  				best_min = min_group_size[axis];  				best_axis = axis; diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index 8066a59281..48984c4d5b 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -35,17 +35,17 @@  float CameraMatrix::determinant() const {  	return matrix[0][3] * matrix[1][2] * matrix[2][1] * matrix[3][0] - matrix[0][2] * matrix[1][3] * matrix[2][1] * matrix[3][0] - -		   matrix[0][3] * matrix[1][1] * matrix[2][2] * matrix[3][0] + matrix[0][1] * matrix[1][3] * matrix[2][2] * matrix[3][0] + -		   matrix[0][2] * matrix[1][1] * matrix[2][3] * matrix[3][0] - matrix[0][1] * matrix[1][2] * matrix[2][3] * matrix[3][0] - -		   matrix[0][3] * matrix[1][2] * matrix[2][0] * matrix[3][1] + matrix[0][2] * matrix[1][3] * matrix[2][0] * matrix[3][1] + -		   matrix[0][3] * matrix[1][0] * matrix[2][2] * matrix[3][1] - matrix[0][0] * matrix[1][3] * matrix[2][2] * matrix[3][1] - -		   matrix[0][2] * matrix[1][0] * matrix[2][3] * matrix[3][1] + matrix[0][0] * matrix[1][2] * matrix[2][3] * matrix[3][1] + -		   matrix[0][3] * matrix[1][1] * matrix[2][0] * matrix[3][2] - matrix[0][1] * matrix[1][3] * matrix[2][0] * matrix[3][2] - -		   matrix[0][3] * matrix[1][0] * matrix[2][1] * matrix[3][2] + matrix[0][0] * matrix[1][3] * matrix[2][1] * matrix[3][2] + -		   matrix[0][1] * matrix[1][0] * matrix[2][3] * matrix[3][2] - matrix[0][0] * matrix[1][1] * matrix[2][3] * matrix[3][2] - -		   matrix[0][2] * matrix[1][1] * matrix[2][0] * matrix[3][3] + matrix[0][1] * matrix[1][2] * matrix[2][0] * matrix[3][3] + -		   matrix[0][2] * matrix[1][0] * matrix[2][1] * matrix[3][3] - matrix[0][0] * matrix[1][2] * matrix[2][1] * matrix[3][3] - -		   matrix[0][1] * matrix[1][0] * matrix[2][2] * matrix[3][3] + matrix[0][0] * matrix[1][1] * matrix[2][2] * matrix[3][3]; +			matrix[0][3] * matrix[1][1] * matrix[2][2] * matrix[3][0] + matrix[0][1] * matrix[1][3] * matrix[2][2] * matrix[3][0] + +			matrix[0][2] * matrix[1][1] * matrix[2][3] * matrix[3][0] - matrix[0][1] * matrix[1][2] * matrix[2][3] * matrix[3][0] - +			matrix[0][3] * matrix[1][2] * matrix[2][0] * matrix[3][1] + matrix[0][2] * matrix[1][3] * matrix[2][0] * matrix[3][1] + +			matrix[0][3] * matrix[1][0] * matrix[2][2] * matrix[3][1] - matrix[0][0] * matrix[1][3] * matrix[2][2] * matrix[3][1] - +			matrix[0][2] * matrix[1][0] * matrix[2][3] * matrix[3][1] + matrix[0][0] * matrix[1][2] * matrix[2][3] * matrix[3][1] + +			matrix[0][3] * matrix[1][1] * matrix[2][0] * matrix[3][2] - matrix[0][1] * matrix[1][3] * matrix[2][0] * matrix[3][2] - +			matrix[0][3] * matrix[1][0] * matrix[2][1] * matrix[3][2] + matrix[0][0] * matrix[1][3] * matrix[2][1] * matrix[3][2] + +			matrix[0][1] * matrix[1][0] * matrix[2][3] * matrix[3][2] - matrix[0][0] * matrix[1][1] * matrix[2][3] * matrix[3][2] - +			matrix[0][2] * matrix[1][1] * matrix[2][0] * matrix[3][3] + matrix[0][1] * matrix[1][2] * matrix[2][0] * matrix[3][3] + +			matrix[0][2] * matrix[1][0] * matrix[2][1] * matrix[3][3] - matrix[0][0] * matrix[1][2] * matrix[2][1] * matrix[3][3] - +			matrix[0][1] * matrix[1][0] * matrix[2][2] * matrix[3][3] + matrix[0][0] * matrix[1][1] * matrix[2][2] * matrix[3][3];  }  void CameraMatrix::set_identity() { diff --git a/core/math/color.cpp b/core/math/color.cpp index dc86cacf8f..8310c342ed 100644 --- a/core/math/color.cpp +++ b/core/math/color.cpp @@ -107,6 +107,39 @@ uint64_t Color::to_rgba64() const {  	return c;  } +String _to_hex(float p_val) { +	int v = Math::round(p_val * 255); +	v = CLAMP(v, 0, 255); +	String ret; + +	for (int i = 0; i < 2; i++) { +		char32_t c[2] = { 0, 0 }; +		int lv = v & 0xF; +		if (lv < 10) { +			c[0] = '0' + lv; +		} else { +			c[0] = 'a' + lv - 10; +		} + +		v >>= 4; +		String cs = (const char32_t *)c; +		ret = cs + ret; +	} + +	return ret; +} + +String Color::to_html(bool p_alpha) const { +	String txt; +	txt += _to_hex(r); +	txt += _to_hex(g); +	txt += _to_hex(b); +	if (p_alpha) { +		txt += _to_hex(a); +	} +	return txt; +} +  float Color::get_h() const {  	float min = MIN(r, g);  	min = MIN(min, b); @@ -249,20 +282,6 @@ Color Color::hex64(uint64_t p_hex) {  	return Color(r, g, b, a);  } -Color Color::from_rgbe9995(uint32_t p_rgbe) { -	float r = p_rgbe & 0x1ff; -	float g = (p_rgbe >> 9) & 0x1ff; -	float b = (p_rgbe >> 18) & 0x1ff; -	float e = (p_rgbe >> 27); -	float m = Math::pow(2, e - 15.0 - 9.0); - -	float rd = r * m; -	float gd = g * m; -	float bd = b * m; - -	return Color(rd, gd, bd, 1.0f); -} -  static int _parse_col4(const String &p_str, int p_ofs) {  	char character = p_str[p_ofs]; @@ -428,43 +447,24 @@ Color Color::from_string(const String &p_string, const Color &p_default) {  	}  } -String _to_hex(float p_val) { -	int v = Math::round(p_val * 255); -	v = CLAMP(v, 0, 255); -	String ret; - -	for (int i = 0; i < 2; i++) { -		char32_t c[2] = { 0, 0 }; -		int lv = v & 0xF; -		if (lv < 10) { -			c[0] = '0' + lv; -		} else { -			c[0] = 'a' + lv - 10; -		} - -		v >>= 4; -		String cs = (const char32_t *)c; -		ret = cs + ret; -	} - -	return ret; +Color Color::from_hsv(float p_h, float p_s, float p_v, float p_alpha) { +	Color c; +	c.set_hsv(p_h, p_s, p_v, p_alpha); +	return c;  } -String Color::to_html(bool p_alpha) const { -	String txt; -	txt += _to_hex(r); -	txt += _to_hex(g); -	txt += _to_hex(b); -	if (p_alpha) { -		txt += _to_hex(a); -	} -	return txt; -} +Color Color::from_rgbe9995(uint32_t p_rgbe) { +	float r = p_rgbe & 0x1ff; +	float g = (p_rgbe >> 9) & 0x1ff; +	float b = (p_rgbe >> 18) & 0x1ff; +	float e = (p_rgbe >> 27); +	float m = Math::pow(2, e - 15.0 - 9.0); -Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const { -	Color c; -	c.set_hsv(p_h, p_s, p_v, p_a); -	return c; +	float rd = r * m; +	float gd = g * m; +	float bd = b * m; + +	return Color(rd, gd, bd, 1.0f);  }  Color::operator String() const { diff --git a/core/math/color.h b/core/math/color.h index a95dbf4f60..ffd0fd8f6e 100644 --- a/core/math/color.h +++ b/core/math/color.h @@ -51,6 +51,7 @@ struct Color {  	uint64_t to_rgba64() const;  	uint64_t to_argb64() const;  	uint64_t to_abgr64() const; +	String to_html(bool p_alpha = true) const;  	float get_h() const;  	float get_s() const;  	float get_v() const; @@ -189,8 +190,7 @@ struct Color {  	static String get_named_color_name(int p_idx);  	static Color get_named_color(int p_idx);  	static Color from_string(const String &p_string, const Color &p_default); -	String to_html(bool p_alpha = true) const; -	Color from_hsv(float p_h, float p_s, float p_v, float p_a) const; +	static Color from_hsv(float p_h, float p_s, float p_v, float p_alpha = 1.0);  	static Color from_rgbe9995(uint32_t p_rgbe);  	_FORCE_INLINE_ bool operator<(const Color &p_color) const; //used in set keys diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index 21cb0efe20..6f551319df 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -265,8 +265,7 @@ public:  		}  		int32_t get_sign() const { -			return ((int64_t)high < 0) ? -1 : (high || low) ? 1 : -																0; +			return ((int64_t)high < 0) ? -1 : ((high || low) ? 1 : 0);  		}  		bool operator<(const Int128 &b) const { @@ -594,8 +593,6 @@ private:  		IntermediateHull() {  		} - -		void print();  	};  	enum Orientation { NONE, @@ -609,9 +606,9 @@ private:  	PagedAllocator<Face> face_pool;  	LocalVector<Vertex *> original_vertices;  	int32_t merge_stamp = 0; -	int32_t min_axis = 0; -	int32_t med_axis = 0; -	int32_t max_axis = 0; +	Vector3::Axis min_axis = Vector3::Axis::AXIS_X; +	Vector3::Axis med_axis = Vector3::Axis::AXIS_X; +	Vector3::Axis max_axis = Vector3::Axis::AXIS_X;  	int32_t used_edge_pairs = 0;  	int32_t max_used_edge_pairs = 0; @@ -737,8 +734,6 @@ int32_t ConvexHullInternal::Rational64::compare(const Rational64 &b) const {  		return 0;  	} -	//	return (numerator * b.denominator > b.numerator * denominator) ? sign : (numerator * b.denominator < b.numerator * denominator) ? -sign : 0; -  #ifdef USE_X86_64_ASM  	int32_t result; @@ -759,10 +754,9 @@ int32_t ConvexHullInternal::Rational64::compare(const Rational64 &b) const {  			: "=&b"(result), [tmp] "=&r"(tmp), "=a"(dummy)  			: "a"(denominator), [bn] "g"(b.numerator), [tn] "g"(numerator), [bd] "g"(b.denominator)  			: "%rdx", "cc"); -	return result ? result ^ sign // if sign is +1, only bit 0 of result is inverted, which does not change the sign of result (and cannot result in zero) -					// if sign is -1, all bits of result are inverted, which changes the sign of result (and again cannot result in zero) -					: -					  0; +	// if sign is +1, only bit 0 of result is inverted, which does not change the sign of result (and cannot result in zero) +	// if sign is -1, all bits of result are inverted, which changes the sign of result (and again cannot result in zero) +	return result ? result ^ sign : 0;  #else @@ -795,8 +789,7 @@ int32_t ConvexHullInternal::Rational128::compare(const Rational128 &b) const {  int32_t ConvexHullInternal::Rational128::compare(int64_t b) const {  	if (is_int_64) {  		int64_t a = sign * (int64_t)numerator.low; -		return (a > b) ? 1 : (a < b) ? -1 : -										 0; +		return (a > b) ? 1 : ((a < b) ? -1 : 0);  	}  	if (b > 0) {  		if (sign <= 0) { @@ -1448,8 +1441,7 @@ void ConvexHullInternal::merge(IntermediateHull &p_h0, IntermediateHull &p_h1) {  			c1->edges = e;  			return;  		} else { -			int32_t cmp = !min0 ? 1 : !min1 ? -1 : -												min_cot0.compare(min_cot1); +			int32_t cmp = !min0 ? 1 : (!min1 ? -1 : min_cot0.compare(min_cot1));  #ifdef DEBUG_CONVEX_HULL  			printf("    -> Result %d\n", cmp);  #endif @@ -1593,12 +1585,12 @@ void ConvexHullInternal::compute(const Vector3 *p_coords, int32_t p_count) {  	}  	Vector3 s = aabb.size; -	max_axis = s.max_axis(); -	min_axis = s.min_axis(); +	max_axis = s.max_axis_index(); +	min_axis = s.min_axis_index();  	if (min_axis == max_axis) { -		min_axis = (max_axis + 1) % 3; +		min_axis = Vector3::Axis((max_axis + 1) % 3);  	} -	med_axis = 3 - max_axis - min_axis; +	med_axis = Vector3::Axis(3 - max_axis - min_axis);  	s /= real_t(10216);  	if (((med_axis + 1) % 3) != max_axis) { @@ -1696,7 +1688,7 @@ real_t ConvexHullInternal::shrink(real_t p_amount, real_t p_clamp_amount) {  	while (stack.size() > 0) {  		Vertex *v = stack[stack.size() - 1]; -		stack.remove(stack.size() - 1); +		stack.remove_at(stack.size() - 1);  		Edge *e = v->edges;  		if (e) {  			do { @@ -2260,10 +2252,21 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3  	r_mesh.vertices = ch.vertices; -	r_mesh.edges.resize(ch.edges.size()); +	// Copy the edges over. There's two "half-edges" for every edge, so we pick only one of them. +	r_mesh.edges.resize(ch.edges.size() / 2); +	uint32_t edges_copied = 0;  	for (uint32_t i = 0; i < ch.edges.size(); i++) { -		r_mesh.edges.write[i].a = (&ch.edges[i])->get_source_vertex(); -		r_mesh.edges.write[i].b = (&ch.edges[i])->get_target_vertex(); +		uint32_t a = (&ch.edges[i])->get_source_vertex(); +		uint32_t b = (&ch.edges[i])->get_target_vertex(); +		if (a < b) { // Copy only the "canonical" edge. For the reverse edge, this will be false. +			ERR_BREAK(edges_copied >= (uint32_t)r_mesh.edges.size()); +			r_mesh.edges.write[edges_copied].a = a; +			r_mesh.edges.write[edges_copied].b = b; +			edges_copied++; +		} +	} +	if (edges_copied != (uint32_t)r_mesh.edges.size()) { +		ERR_PRINT("Invalid edge count.");  	}  	r_mesh.faces.resize(ch.faces.size()); diff --git a/core/math/convex_hull.h b/core/math/convex_hull.h index a860d60b02..806c6cc3fb 100644 --- a/core/math/convex_hull.h +++ b/core/math/convex_hull.h @@ -49,7 +49,7 @@ subject to the following restrictions:  #include "core/templates/vector.h"  /// Convex hull implementation based on Preparata and Hong -/// See https://code.google.com/p/bullet/issues/detail?id=275 +/// See https://code.google.com/archive/p/bullet/issues/275  /// Ole Kniemeyer, MAXON Computer GmbH  class ConvexHullComputer {  public: diff --git a/core/math/delaunay_2d.h b/core/math/delaunay_2d.h index 95064e5700..779ac96b79 100644 --- a/core/math/delaunay_2d.h +++ b/core/math/delaunay_2d.h @@ -101,7 +101,7 @@ public:  		}  		float delta_max = MAX(rect.size.width, rect.size.height); -		Vector2 center = rect.position + rect.size * 0.5; +		Vector2 center = rect.get_center();  		points.push_back(Vector2(center.x - 20 * delta_max, center.y - delta_max));  		points.push_back(Vector2(center.x, center.y + 20 * delta_max)); @@ -123,7 +123,7 @@ public:  			for (int j = 0; j < triangles.size(); j++) {  				if (triangles[j].bad) { -					triangles.remove(j); +					triangles.remove_at(j);  					j--;  				}  			} @@ -154,7 +154,7 @@ public:  				}  			}  			if (invalid) { -				triangles.remove(i); +				triangles.remove_at(i);  				i--;  			}  		} diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h index d63132b4da..0b6286cd9d 100644 --- a/core/math/dynamic_bvh.h +++ b/core/math/dynamic_bvh.h @@ -41,7 +41,7 @@  /*  Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2013 Erwin Coumans  https://bulletphysics.org +Copyright (c) 2003-2013 Erwin Coumans  http://bulletphysics.org  This software is provided 'as-is', without any express or implied warranty.  In no event will the authors be held liable for any damages arising from the use of this software. diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 05f2c8dac9..fe277cff96 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -410,6 +410,14 @@ Error Expression::_get_token(Token &r_token) {  					} else if (id == "self") {  						r_token.type = TK_SELF;  					} else { +						for (int i = 0; i < Variant::VARIANT_MAX; i++) { +							if (id == Variant::get_type_name(Variant::Type(i))) { +								r_token.type = TK_BASIC_TYPE; +								r_token.value = i; +								return OK; +							} +						} +  						if (Variant::has_utility_function(id)) {  							r_token.type = TK_BUILTIN_FUNC;  							r_token.value = id; @@ -1087,7 +1095,7 @@ Expression::ENode *Expression::_parse_expression() {  				op->nodes[1] = nullptr;  				expression.write[i].is_op = false;  				expression.write[i].node = op; -				expression.remove(i + 1); +				expression.remove_at(i + 1);  			}  		} else { @@ -1119,8 +1127,8 @@ Expression::ENode *Expression::_parse_expression() {  			//replace all 3 nodes by this operator and make it an expression  			expression.write[next_op - 1].node = op; -			expression.remove(next_op); -			expression.remove(next_op); +			expression.remove_at(next_op); +			expression.remove_at(next_op);  		}  	} diff --git a/core/math/face3.cpp b/core/math/face3.cpp index 9af3f868d2..31a853e1a9 100644 --- a/core/math/face3.cpp +++ b/core/math/face3.cpp @@ -151,8 +151,8 @@ Face3::Side Face3::get_side_of(const Face3 &p_face, ClockDirection p_clock_dir)  }  Vector3 Face3::get_random_point_inside() const { -	real_t a = Math::random(0, 1); -	real_t b = Math::random(0, 1); +	real_t a = Math::random(0.0, 1.0); +	real_t b = Math::random(0.0, 1.0);  	if (a > b) {  		SWAP(a, b);  	} @@ -229,7 +229,7 @@ bool Face3::intersects_aabb(const AABB &p_aabb) const {  			axis.normalize();  			real_t minA, maxA, minB, maxB; -			p_aabb.project_range_in_plane(Plane(axis, 0), minA, maxA); +			p_aabb.project_range_in_plane(Plane(axis), minA, maxA);  			project_range(axis, Transform3D(), minB, maxB);  			if (maxA < minB || maxB < minA) { diff --git a/core/math/face3.h b/core/math/face3.h index 9e9026e54e..0a8c1c6041 100644 --- a/core/math/face3.h +++ b/core/math/face3.h @@ -48,13 +48,13 @@ public:  	Vector3 vertex[3];  	/** -         * -         * @param p_plane plane used to split the face -         * @param p_res array of at least 3 faces, amount used in function return -         * @param p_is_point_over array of at least 3 booleans, determining which face is over the plane, amount used in function return -         * @param _epsilon constant used for numerical error rounding, to add "thickness" to the plane (so coplanar points can happen) -         * @return amount of faces generated by the split, either 0 (means no split possible), 2 or 3 -         */ +	 * +	 * @param p_plane plane used to split the face +	 * @param p_res array of at least 3 faces, amount used in function return +	 * @param p_is_point_over array of at least 3 booleans, determining which face is over the plane, amount used in function return +	 * @param _epsilon constant used for numerical error rounding, to add "thickness" to the plane (so coplanar points can happen) +	 * @return amount of faces generated by the split, either 0 (means no split possible), 2 or 3 +	 */  	int split_by_plane(const Plane &p_plane, Face3 *p_res, bool *p_is_point_over) const; diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h index 8e5830f9b3..028ac0f4eb 100644 --- a/core/math/geometry_2d.h +++ b/core/math/geometry_2d.h @@ -37,8 +37,6 @@  #include "core/templates/vector.h"  class Geometry2D { -	Geometry2D(); -  public:  	static real_t get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2, Vector2 &c1, Vector2 &c2) {  		Vector2 d1 = q1 - p1; // Direction vector of segment S1. @@ -183,8 +181,7 @@ public:  		D = Vector2(D.x * Bn.x + D.y * Bn.y, D.y * Bn.x - D.x * Bn.y);  		// Fail if C x B and D x B have the same sign (segments don't intersect). -		// (equivalent to condition (C.y < 0 && D.y < CMP_EPSILON) || (C.y > 0 && D.y > CMP_EPSILON)) -		if (C.y * D.y > CMP_EPSILON) { +		if ((C.y < -CMP_EPSILON && D.y < -CMP_EPSILON) || (C.y > CMP_EPSILON && D.y > CMP_EPSILON)) {  			return false;  		} diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp index 6628b760e0..88d2656025 100644 --- a/core/math/geometry_3d.cpp +++ b/core/math/geometry_3d.cpp @@ -819,11 +819,9 @@ Vector<Plane> Geometry3D::build_sphere_planes(real_t p_radius, int p_lats, int p  		planes.push_back(Plane(normal, p_radius));  		for (int j = 1; j <= p_lats; j++) { -			// FIXME: This is stupid. -			Vector3 angle = normal.lerp(axis, j / (real_t)p_lats).normalized(); -			Vector3 pos = angle * p_radius; -			planes.push_back(Plane(pos, angle)); -			planes.push_back(Plane(pos * axis_neg, angle * axis_neg)); +			Vector3 plane_normal = normal.lerp(axis, j / (real_t)p_lats).normalized(); +			planes.push_back(Plane(plane_normal, p_radius)); +			planes.push_back(Plane(plane_normal * axis_neg, p_radius));  		}  	} @@ -852,10 +850,10 @@ Vector<Plane> Geometry3D::build_capsule_planes(real_t p_radius, real_t p_height,  		planes.push_back(Plane(normal, p_radius));  		for (int j = 1; j <= p_lats; j++) { -			Vector3 angle = normal.lerp(axis, j / (real_t)p_lats).normalized(); -			Vector3 pos = axis * p_height * 0.5 + angle * p_radius; -			planes.push_back(Plane(pos, angle)); -			planes.push_back(Plane(pos * axis_neg, angle * axis_neg)); +			Vector3 plane_normal = normal.lerp(axis, j / (real_t)p_lats).normalized(); +			Vector3 position = axis * p_height * 0.5 + plane_normal * p_radius; +			planes.push_back(Plane(plane_normal, position)); +			planes.push_back(Plane(plane_normal * axis_neg, position * axis_neg));  		}  	} diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h index 766689e222..6a59b34585 100644 --- a/core/math/geometry_3d.h +++ b/core/math/geometry_3d.h @@ -36,8 +36,6 @@  #include "core/templates/vector.h"  class Geometry3D { -	Geometry3D(); -  public:  	static void get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2, Vector3 &c1, Vector3 &c2) {  // Do the function 'd' as defined by pb. I think it's a dot product of some sort. diff --git a/core/math/math_defs.h b/core/math/math_defs.h index c3a8f910c0..1c6139688b 100644 --- a/core/math/math_defs.h +++ b/core/math/math_defs.h @@ -68,37 +68,38 @@ enum Orientation {  	VERTICAL  }; -enum HAlign { -	HALIGN_LEFT, -	HALIGN_CENTER, -	HALIGN_RIGHT, -	HALIGN_FILL, +enum HorizontalAlignment { +	HORIZONTAL_ALIGNMENT_LEFT, +	HORIZONTAL_ALIGNMENT_CENTER, +	HORIZONTAL_ALIGNMENT_RIGHT, +	HORIZONTAL_ALIGNMENT_FILL,  }; -enum VAlign { -	VALIGN_TOP, -	VALIGN_CENTER, -	VALIGN_BOTTOM +enum VerticalAlignment { +	VERTICAL_ALIGNMENT_TOP, +	VERTICAL_ALIGNMENT_CENTER, +	VERTICAL_ALIGNMENT_BOTTOM, +	VERTICAL_ALIGNMENT_FILL,  }; -enum InlineAlign { +enum InlineAlignment {  	// Image alignment points. -	INLINE_ALIGN_TOP_TO = 0b0000, -	INLINE_ALIGN_CENTER_TO = 0b0001, -	INLINE_ALIGN_BOTTOM_TO = 0b0010, -	INLINE_ALIGN_IMAGE_MASK = 0b0011, +	INLINE_ALIGNMENT_TOP_TO = 0b0000, +	INLINE_ALIGNMENT_CENTER_TO = 0b0001, +	INLINE_ALIGNMENT_BOTTOM_TO = 0b0010, +	INLINE_ALIGNMENT_IMAGE_MASK = 0b0011,  	// Text alignment points. -	INLINE_ALIGN_TO_TOP = 0b0000, -	INLINE_ALIGN_TO_CENTER = 0b0100, -	INLINE_ALIGN_TO_BASELINE = 0b1000, -	INLINE_ALIGN_TO_BOTTOM = 0b1100, -	INLINE_ALIGN_TEXT_MASK = 0b1100, +	INLINE_ALIGNMENT_TO_TOP = 0b0000, +	INLINE_ALIGNMENT_TO_CENTER = 0b0100, +	INLINE_ALIGNMENT_TO_BASELINE = 0b1000, +	INLINE_ALIGNMENT_TO_BOTTOM = 0b1100, +	INLINE_ALIGNMENT_TEXT_MASK = 0b1100,  	// Presets. -	INLINE_ALIGN_TOP = INLINE_ALIGN_TOP_TO | INLINE_ALIGN_TO_TOP, -	INLINE_ALIGN_CENTER = INLINE_ALIGN_CENTER_TO | INLINE_ALIGN_TO_CENTER, -	INLINE_ALIGN_BOTTOM = INLINE_ALIGN_BOTTOM_TO | INLINE_ALIGN_TO_BOTTOM +	INLINE_ALIGNMENT_TOP = INLINE_ALIGNMENT_TOP_TO | INLINE_ALIGNMENT_TO_TOP, +	INLINE_ALIGNMENT_CENTER = INLINE_ALIGNMENT_CENTER_TO | INLINE_ALIGNMENT_TO_CENTER, +	INLINE_ALIGNMENT_BOTTOM = INLINE_ALIGNMENT_BOTTOM_TO | INLINE_ALIGNMENT_TO_BOTTOM  };  enum Side { @@ -116,10 +117,10 @@ enum Corner {  };  /** -  * The "Real" type is an abstract type used for real numbers, such as 1.5, -  * in contrast to integer numbers. Precision can be controlled with the -  * presence or absence of the REAL_T_IS_DOUBLE define. -  */ + * The "Real" type is an abstract type used for real numbers, such as 1.5, + * in contrast to integer numbers. Precision can be controlled with the + * presence or absence of the REAL_T_IS_DOUBLE define. + */  #ifdef REAL_T_IS_DOUBLE  typedef double real_t;  #else diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index bbed257f60..2b6d92fe0e 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -53,6 +53,10 @@ uint32_t Math::rand() {  	return default_rand.rand();  } +double Math::randfn(double mean, double deviation) { +	return default_rand.randfn(mean, deviation); +} +  int Math::step_decimals(double p_step) {  	static const int maxn = 10;  	static const double sd[maxn] = { diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 4e4f566517..8df45255c9 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -159,7 +159,7 @@ public:  		} ieee754;  		ieee754.f = p_val;  		return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && -			   ((unsigned)ieee754.u == 0); +				((unsigned)ieee754.u == 0);  #else  		return isinf(p_val);  #endif @@ -266,8 +266,8 @@ public:  		float s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0f, 1.0f);  		return s * s * (3.0f - 2.0f * s);  	} -	static _ALWAYS_INLINE_ double move_toward(double p_from, double p_to, double p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + 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; } +	static _ALWAYS_INLINE_ double move_toward(double p_from, double p_to, double p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SIGN(p_to - p_from) * p_delta; } +	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 + SIGN(p_to - p_from) * p_delta; }  	static _ALWAYS_INLINE_ double linear2db(double p_linear) { return Math::log(p_linear) * 8.6858896380650365530225783783321; }  	static _ALWAYS_INLINE_ float linear2db(float p_linear) { return Math::log(p_linear) * 8.6858896380650365530225783783321; } @@ -291,6 +291,19 @@ public:  		return is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range));  	} +	static _ALWAYS_INLINE_ float fract(float value) { +		return value - floor(value); +	} +	static _ALWAYS_INLINE_ double fract(double value) { +		return value - floor(value); +	} +	static _ALWAYS_INLINE_ float pingpong(float value, float length) { +		return (length != 0.0f) ? abs(fract((value - length) / (length * 2.0f)) * length * 2.0f - length) : 0.0f; +	} +	static _ALWAYS_INLINE_ double pingpong(double value, double length) { +		return (length != 0.0) ? abs(fract((value - length) / (length * 2.0)) * length * 2.0 - length) : 0.0; +	} +  	// double only, as these functions are mainly used by the editor and not performance-critical,  	static double ease(double p_x, double p_c);  	static int step_decimals(double p_step); @@ -305,6 +318,7 @@ public:  	static uint32_t rand();  	static _ALWAYS_INLINE_ double randd() { return (double)rand() / (double)Math::RANDOM_32BIT_MAX; }  	static _ALWAYS_INLINE_ float randf() { return (float)rand() / (float)Math::RANDOM_32BIT_MAX; } +	static double randfn(double mean, double deviation);  	static double random(double from, double to);  	static float random(float from, float to); @@ -461,7 +475,7 @@ public:  				mantissa = 0;  			}  			hf = (((uint16_t)sign) << 15) | (uint16_t)((0x1F << 10)) | -				 (uint16_t)(mantissa >> 13); +					(uint16_t)(mantissa >> 13);  		}  		// check if exponent is <= -15  		else if (exp <= 0x38000000) { @@ -474,8 +488,8 @@ public:  			hf = 0; //denormals do not work for 3D, convert to zero  		} else {  			hf = (((uint16_t)sign) << 15) | -				 (uint16_t)((exp - 0x38000000) >> 13) | -				 (uint16_t)(mantissa >> 13); +					(uint16_t)((exp - 0x38000000) >> 13) | +					(uint16_t)(mantissa >> 13);  		}  		return hf; diff --git a/core/math/plane.cpp b/core/math/plane.cpp index 3c78b55b90..59f7918258 100644 --- a/core/math/plane.cpp +++ b/core/math/plane.cpp @@ -88,7 +88,7 @@ bool Plane::intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r  		*r_result = ((vec3_cross(normal1, normal2) * p_plane0.d) +  							(vec3_cross(normal2, normal0) * p_plane1.d) +  							(vec3_cross(normal0, normal1) * p_plane2.d)) / -					denom; +				denom;  	}  	return true; diff --git a/core/math/plane.h b/core/math/plane.h index 2267b28c53..18be5d5d12 100644 --- a/core/math/plane.h +++ b/core/math/plane.h @@ -85,8 +85,8 @@ public:  			normal(p_a, p_b, p_c),  			d(p_d) {} -	_FORCE_INLINE_ Plane(const Vector3 &p_normal, real_t p_d); -	_FORCE_INLINE_ Plane(const Vector3 &p_point, const Vector3 &p_normal); +	_FORCE_INLINE_ Plane(const Vector3 &p_normal, real_t p_d = 0.0); +	_FORCE_INLINE_ Plane(const Vector3 &p_normal, const Vector3 &p_point);  	_FORCE_INLINE_ Plane(const Vector3 &p_point1, const Vector3 &p_point2, const Vector3 &p_point3, ClockDirection p_dir = CLOCKWISE);  }; @@ -109,7 +109,7 @@ Plane::Plane(const Vector3 &p_normal, real_t p_d) :  		d(p_d) {  } -Plane::Plane(const Vector3 &p_point, const Vector3 &p_normal) : +Plane::Plane(const Vector3 &p_normal, const Vector3 &p_point) :  		normal(p_normal),  		d(p_normal.dot(p_point)) {  } diff --git a/core/math/quaternion.cpp b/core/math/quaternion.cpp index 3f1d2c58e5..944474686a 100644 --- a/core/math/quaternion.cpp +++ b/core/math/quaternion.cpp @@ -44,7 +44,7 @@ real_t Quaternion::angle_to(const Quaternion &p_to) const {  // This implementation uses XYZ convention (Z is the first rotation).  Vector3 Quaternion::get_euler_xyz() const {  	Basis m(*this); -	return m.get_euler_xyz(); +	return m.get_euler(Basis::EULER_ORDER_XYZ);  }  // get_euler_yxz returns a vector containing the Euler angles in the format @@ -56,7 +56,7 @@ Vector3 Quaternion::get_euler_yxz() const {  	ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion must be normalized.");  #endif  	Basis m(*this); -	return m.get_euler_yxz(); +	return m.get_euler(Basis::EULER_ORDER_YXZ);  }  void Quaternion::operator*=(const Quaternion &p_q) { @@ -189,6 +189,15 @@ Quaternion::operator String() const {  	return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ", " + String::num_real(w, false) + ")";  } +Vector3 Quaternion::get_axis() const { +	real_t r = ((real_t)1) / Math::sqrt(1 - w * w); +	return Vector3(x * r, y * r, z * r); +} + +float Quaternion::get_angle() const { +	return 2 * Math::acos(w); +} +  Quaternion::Quaternion(const Vector3 &p_axis, real_t p_angle) {  #ifdef MATH_CHECKS  	ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 must be normalized."); diff --git a/core/math/quaternion.h b/core/math/quaternion.h index 35324323b3..457d167516 100644 --- a/core/math/quaternion.h +++ b/core/math/quaternion.h @@ -72,6 +72,9 @@ public:  	Quaternion slerpni(const Quaternion &p_to, const real_t &p_weight) const;  	Quaternion cubic_slerp(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const; +	Vector3 get_axis() const; +	float get_angle() const; +  	_FORCE_INLINE_ void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {  		r_angle = 2 * Math::acos(w);  		real_t r = ((real_t)1) / Math::sqrt(1 - w * w); @@ -83,13 +86,6 @@ public:  	void operator*=(const Quaternion &p_q);  	Quaternion operator*(const Quaternion &p_q) const; -	Quaternion operator*(const Vector3 &v) const { -		return Quaternion(w * v.x + y * v.z - z * v.y, -				w * v.y + z * v.x - x * v.z, -				w * v.z + x * v.y - y * v.x, -				-x * v.x - y * v.y - z * v.z); -	} -  	_FORCE_INLINE_ Vector3 xform(const Vector3 &v) const {  #ifdef MATH_CHECKS  		ERR_FAIL_COND_V_MSG(!is_normalized(), v, "The quaternion must be normalized."); @@ -138,12 +134,11 @@ public:  			w(p_q.w) {  	} -	Quaternion &operator=(const Quaternion &p_q) { +	void operator=(const Quaternion &p_q) {  		x = p_q.x;  		y = p_q.y;  		z = p_q.z;  		w = p_q.w; -		return *this;  	}  	Quaternion(const Vector3 &v0, const Vector3 &v1) // shortest arc diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index 0960fe19a6..d438a9a377 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -265,8 +265,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_  		//create new faces from horizon edges  		List<List<Face>::Element *> new_faces; //new faces -		for (Map<Edge, FaceConnect>::Element *E = lit_edges.front(); E; E = E->next()) { -			FaceConnect &fc = E->get(); +		for (KeyValue<Edge, FaceConnect> &E : lit_edges) { +			FaceConnect &fc = E.value;  			if (fc.left && fc.right) {  				continue; //edge is uninteresting, not on horizon  			} @@ -275,8 +275,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_  			Face face;  			face.vertices[0] = f.points_over[next]; -			face.vertices[1] = E->key().vertices[0]; -			face.vertices[2] = E->key().vertices[1]; +			face.vertices[1] = E.key.vertices[0]; +			face.vertices[2] = E.key.vertices[1];  			Plane p(p_points[face.vertices[0]], p_points[face.vertices[1]], p_points[face.vertices[2]]); @@ -418,13 +418,13 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_  				}  				// remove all edge connections to this face -				for (Map<Edge, RetFaceConnect>::Element *G = ret_edges.front(); G; G = G->next()) { -					if (G->get().left == O) { -						G->get().left = nullptr; +				for (KeyValue<Edge, RetFaceConnect> &G : ret_edges) { +					if (G.value.left == O) { +						G.value.left = nullptr;  					} -					if (G->get().right == O) { -						G->get().right = nullptr; +					if (G.value.right == O) { +						G.value.right = nullptr;  					}  				} @@ -444,10 +444,10 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_  	}  	r_mesh.edges.resize(ret_edges.size());  	idx = 0; -	for (Map<Edge, RetFaceConnect>::Element *E = ret_edges.front(); E; E = E->next()) { +	for (const KeyValue<Edge, RetFaceConnect> &E : ret_edges) {  		Geometry3D::MeshData::Edge e; -		e.a = E->key().vertices[0]; -		e.b = E->key().vertices[1]; +		e.a = E.key.vertices[0]; +		e.b = E.key.vertices[1];  		r_mesh.edges.write[idx++] = e;  	} diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp index f64bf560c8..0e6127b017 100644 --- a/core/math/rect2.cpp +++ b/core/math/rect2.cpp @@ -35,6 +35,11 @@ bool Rect2::is_equal_approx(const Rect2 &p_rect) const {  }  bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const { +#ifdef MATH_CHECKS +	if (unlikely(size.x < 0 || size.y < 0)) { +		ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); +	} +#endif  	real_t min = 0, max = 1;  	int axis = 0;  	real_t sign = 0; @@ -95,6 +100,11 @@ bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2  }  bool Rect2::intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const { +#ifdef MATH_CHECKS +	if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { +		ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); +	} +#endif  	//SAT intersection between local and transformed rect2  	Vector2 xf_points[4] = { diff --git a/core/math/rect2.h b/core/math/rect2.h index ab0b489b4a..7029204cf1 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -46,7 +46,14 @@ struct Rect2 {  	real_t get_area() const { return size.width * size.height; } +	_FORCE_INLINE_ Vector2 get_center() const { return position + (size * 0.5); } +  	inline bool intersects(const Rect2 &p_rect, const bool p_include_borders = false) const { +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { +			ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); +		} +#endif  		if (p_include_borders) {  			if (position.x > (p_rect.position.x + p_rect.size.width)) {  				return false; @@ -79,6 +86,11 @@ struct Rect2 {  	}  	inline real_t distance_to(const Vector2 &p_point) const { +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0)) { +			ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); +		} +#endif  		real_t dist = 0.0;  		bool inside = true; @@ -115,9 +127,14 @@ struct Rect2 {  	bool intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos = nullptr, Point2 *r_normal = nullptr) const;  	inline bool encloses(const Rect2 &p_rect) const { +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { +			ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); +		} +#endif  		return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && -			   ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) && -			   ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y)); +				((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) && +				((p_rect.position.y + p_rect.size.y) <= (position.y + size.y));  	}  	_FORCE_INLINE_ bool has_no_area() const { @@ -145,7 +162,11 @@ struct Rect2 {  	}  	inline Rect2 merge(const Rect2 &p_rect) const { ///< return a merged rect - +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { +			ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); +		} +#endif  		Rect2 new_rect;  		new_rect.position.x = MIN(p_rect.position.x, position.x); @@ -159,6 +180,11 @@ struct Rect2 {  		return new_rect;  	}  	inline bool has_point(const Point2 &p_point) const { +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0)) { +			ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); +		} +#endif  		if (p_point.x < position.x) {  			return false;  		} @@ -181,6 +207,11 @@ struct Rect2 {  	bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; }  	inline Rect2 grow(real_t p_amount) const { +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0)) { +			ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); +		} +#endif  		Rect2 g = *this;  		g.grow_by(p_amount);  		return g; @@ -207,6 +238,11 @@ struct Rect2 {  	}  	inline Rect2 grow_individual(real_t p_left, real_t p_top, real_t p_right, real_t p_bottom) const { +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0)) { +			ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); +		} +#endif  		Rect2 g = *this;  		g.position.x -= p_left;  		g.position.y -= p_top; @@ -223,7 +259,11 @@ struct Rect2 {  	}  	inline void expand_to(const Vector2 &p_vector) { //in place function for speed - +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0)) { +			ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); +		} +#endif  		Vector2 begin = position;  		Vector2 end = position + size; @@ -255,11 +295,11 @@ struct Rect2 {  		return Vector2(  					   (p_normal.x > 0) ? -half_extents.x : half_extents.x,  					   (p_normal.y > 0) ? -half_extents.y : half_extents.y) + -			   ofs; +				ofs;  	}  	_FORCE_INLINE_ bool intersects_filled_polygon(const Vector2 *p_points, int p_point_count) const { -		Vector2 center = position + size * 0.5; +		Vector2 center = get_center();  		int side_plus = 0;  		int side_minus = 0;  		Vector2 end = position + size; @@ -344,7 +384,14 @@ struct Rect2i {  	int get_area() const { return size.width * size.height; } +	_FORCE_INLINE_ Vector2i get_center() const { return position + (size / 2); } +  	inline bool intersects(const Rect2i &p_rect) const { +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { +			ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); +		} +#endif  		if (position.x > (p_rect.position.x + p_rect.size.width)) {  			return false;  		} @@ -362,9 +409,14 @@ struct Rect2i {  	}  	inline bool encloses(const Rect2i &p_rect) const { +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { +			ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); +		} +#endif  		return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && -			   ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && -			   ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); +				((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && +				((p_rect.position.y + p_rect.size.y) < (position.y + size.y));  	}  	_FORCE_INLINE_ bool has_no_area() const { @@ -385,14 +437,18 @@ struct Rect2i {  		Point2i p_rect_end = p_rect.position + p_rect.size;  		Point2i end = position + size; -		new_rect.size.x = (int)(MIN(p_rect_end.x, end.x) - new_rect.position.x); -		new_rect.size.y = (int)(MIN(p_rect_end.y, end.y) - new_rect.position.y); +		new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; +		new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y;  		return new_rect;  	}  	inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect - +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { +			ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); +		} +#endif  		Rect2i new_rect;  		new_rect.position.x = MIN(p_rect.position.x, position.x); @@ -406,6 +462,11 @@ struct Rect2i {  		return new_rect;  	}  	bool has_point(const Point2i &p_point) const { +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0)) { +			ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); +		} +#endif  		if (p_point.x < position.x) {  			return false;  		} @@ -427,6 +488,11 @@ struct Rect2i {  	bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; }  	Rect2i grow(int p_amount) const { +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0)) { +			ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); +		} +#endif  		Rect2i g = *this;  		g.position.x -= p_amount;  		g.position.y -= p_amount; @@ -449,6 +515,11 @@ struct Rect2i {  	}  	inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const { +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0)) { +			ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); +		} +#endif  		Rect2i g = *this;  		g.position.x -= p_left;  		g.position.y -= p_top; @@ -465,6 +536,11 @@ struct Rect2i {  	}  	inline void expand_to(const Point2i &p_vector) { +#ifdef MATH_CHECKS +		if (unlikely(size.x < 0 || size.y < 0)) { +			ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); +		} +#endif  		Point2i begin = position;  		Point2i end = position + size; diff --git a/core/math/static_raycaster.cpp b/core/math/static_raycaster.cpp new file mode 100644 index 0000000000..da05d49428 --- /dev/null +++ b/core/math/static_raycaster.cpp @@ -0,0 +1,40 @@ +/*************************************************************************/ +/*  static_raycaster.cpp                                                 */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2021 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 "static_raycaster.h" + +StaticRaycaster *(*StaticRaycaster::create_function)() = nullptr; + +Ref<StaticRaycaster> StaticRaycaster::create() { +	if (create_function) { +		return Ref<StaticRaycaster>(create_function()); +	} +	return Ref<StaticRaycaster>(); +} diff --git a/core/math/static_raycaster.h b/core/math/static_raycaster.h new file mode 100644 index 0000000000..3759c788a7 --- /dev/null +++ b/core/math/static_raycaster.h @@ -0,0 +1,111 @@ +/*************************************************************************/ +/*  static_raycaster.h                                                   */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2021 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 STATIC_RAYCASTER_H +#define STATIC_RAYCASTER_H + +#include "core/object/ref_counted.h" + +#if !defined(__aligned) + +#if defined(_WIN32) && defined(_MSC_VER) +#define __aligned(...) __declspec(align(__VA_ARGS__)) +#else +#define __aligned(...) __attribute__((aligned(__VA_ARGS__))) +#endif + +#endif + +class StaticRaycaster : public RefCounted { +	GDCLASS(StaticRaycaster, RefCounted) +protected: +	static StaticRaycaster *(*create_function)(); + +public: +	// compatible with embree3 rays +	struct __aligned(16) Ray { +		const static unsigned int INVALID_GEOMETRY_ID = ((unsigned int)-1); // from rtcore_common.h + +		/*! Default construction does nothing. */ +		_FORCE_INLINE_ Ray() : +				geomID(INVALID_GEOMETRY_ID) {} + +		/*! Constructs a ray from origin, direction, and ray segment. Near +		 *  has to be smaller than far. */ +		_FORCE_INLINE_ Ray(const Vector3 &org, +				const Vector3 &dir, +				float tnear = 0.0f, +				float tfar = INFINITY) : +				org(org), +				tnear(tnear), +				dir(dir), +				time(0.0f), +				tfar(tfar), +				mask(-1), +				u(0.0), +				v(0.0), +				primID(INVALID_GEOMETRY_ID), +				geomID(INVALID_GEOMETRY_ID), +				instID(INVALID_GEOMETRY_ID) {} + +		/*! Tests if we hit something. */ +		_FORCE_INLINE_ explicit operator bool() const { return geomID != INVALID_GEOMETRY_ID; } + +	public: +		Vector3 org; //!< Ray origin + tnear +		float tnear; //!< Start of ray segment +		Vector3 dir; //!< Ray direction + tfar +		float time; //!< Time of this ray for motion blur. +		float tfar; //!< End of ray segment +		unsigned int mask; //!< used to mask out objects during traversal +		unsigned int id; //!< ray ID +		unsigned int flags; //!< ray flags + +		Vector3 normal; //!< Not normalized geometry normal +		float u; //!< Barycentric u coordinate of hit +		float v; //!< Barycentric v coordinate of hit +		unsigned int primID; //!< primitive ID +		unsigned int geomID; //!< geometry ID +		unsigned int instID; //!< instance ID +	}; + +	virtual bool intersect(Ray &p_ray) = 0; +	virtual void intersect(Vector<Ray> &r_rays) = 0; + +	virtual void add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) = 0; +	virtual void commit() = 0; + +	virtual void set_mesh_filter(const Set<int> &p_mesh_ids) = 0; +	virtual void clear_mesh_filter() = 0; + +	static Ref<StaticRaycaster> create(); +}; + +#endif // STATIC_RAYCASTER_H diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 16934d67df..4bdeaa2a58 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -63,25 +63,25 @@ Transform2D Transform2D::affine_inverse() const {  	return inv;  } -void Transform2D::rotate(real_t p_phi) { +void Transform2D::rotate(const real_t p_phi) {  	*this = Transform2D(p_phi, Vector2()) * (*this);  }  real_t Transform2D::get_skew() const {  	real_t det = basis_determinant(); -	return Math::acos(elements[0].normalized().dot(SGN(det) * elements[1].normalized())) - Math_PI * 0.5; +	return Math::acos(elements[0].normalized().dot(SIGN(det) * elements[1].normalized())) - Math_PI * 0.5;  } -void Transform2D::set_skew(float p_angle) { +void Transform2D::set_skew(const real_t p_angle) {  	real_t det = basis_determinant(); -	elements[1] = SGN(det) * elements[0].rotated((Math_PI * 0.5 + p_angle)).normalized() * elements[1].length(); +	elements[1] = SIGN(det) * elements[0].rotated((Math_PI * 0.5 + p_angle)).normalized() * elements[1].length();  }  real_t Transform2D::get_rotation() const {  	return Math::atan2(elements[0].y, elements[0].x);  } -void Transform2D::set_rotation(real_t p_rot) { +void Transform2D::set_rotation(const real_t p_rot) {  	Size2 scale = get_scale();  	real_t cr = Math::cos(p_rot);  	real_t sr = Math::sin(p_rot); @@ -92,7 +92,7 @@ void Transform2D::set_rotation(real_t p_rot) {  	set_scale(scale);  } -Transform2D::Transform2D(real_t p_rot, const Vector2 &p_pos) { +Transform2D::Transform2D(const real_t p_rot, const Vector2 &p_pos) {  	real_t cr = Math::cos(p_rot);  	real_t sr = Math::sin(p_rot);  	elements[0][0] = cr; @@ -102,8 +102,16 @@ Transform2D::Transform2D(real_t p_rot, const Vector2 &p_pos) {  	elements[2] = p_pos;  } +Transform2D::Transform2D(const real_t p_rot, const Size2 &p_scale, const real_t p_skew, const Vector2 &p_pos) { +	elements[0][0] = Math::cos(p_rot) * p_scale.x; +	elements[1][1] = Math::cos(p_rot + p_skew) * p_scale.y; +	elements[1][0] = -Math::sin(p_rot + p_skew) * p_scale.y; +	elements[0][1] = Math::sin(p_rot) * p_scale.x; +	elements[2] = p_pos; +} +  Size2 Transform2D::get_scale() const { -	real_t det_sign = SGN(basis_determinant()); +	real_t det_sign = SIGN(basis_determinant());  	return Size2(elements[0].length(), det_sign * elements[1].length());  } @@ -126,7 +134,7 @@ void Transform2D::scale_basis(const Size2 &p_scale) {  	elements[1][1] *= p_scale.y;  } -void Transform2D::translate(real_t p_tx, real_t p_ty) { +void Transform2D::translate(const real_t p_tx, const real_t p_ty) {  	translate(Vector2(p_tx, p_ty));  } @@ -231,7 +239,7 @@ Transform2D Transform2D::translated(const Vector2 &p_offset) const {  	return copy;  } -Transform2D Transform2D::rotated(real_t p_phi) const { +Transform2D Transform2D::rotated(const real_t p_phi) const {  	Transform2D copy = *this;  	copy.rotate(p_phi);  	return copy; @@ -241,7 +249,7 @@ real_t Transform2D::basis_determinant() const {  	return elements[0].x * elements[1].y - elements[0].y * elements[1].x;  } -Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t p_c) const { +Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, const real_t p_c) const {  	//extract parameters  	Vector2 p1 = get_origin();  	Vector2 p2 = p_transform.get_origin(); @@ -271,7 +279,7 @@ Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t  	}  	//construct matrix -	Transform2D res(Math::atan2(v.y, v.x), p1.lerp(p2, p_c)); +	Transform2D res(v.angle(), p1.lerp(p2, p_c));  	res.scale_basis(s1.lerp(s2, p_c));  	return res;  } @@ -290,6 +298,6 @@ Transform2D Transform2D::operator*(const real_t p_val) const {  Transform2D::operator String() const {  	return "[X: " + elements[0].operator String() + -		   ", Y: " + elements[1].operator String() + -		   ", O: " + elements[2].operator String() + "]"; +			", Y: " + elements[1].operator String() + +			", O: " + elements[2].operator String() + "]";  } diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index 34cfd0c1a9..8a0e876d96 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -68,17 +68,17 @@ struct Transform2D {  	void affine_invert();  	Transform2D affine_inverse() const; -	void set_rotation(real_t p_rot); +	void set_rotation(const real_t p_rot);  	real_t get_rotation() const;  	real_t get_skew() const; -	void set_skew(float p_angle); -	_FORCE_INLINE_ void set_rotation_and_scale(real_t p_rot, const Size2 &p_scale); -	_FORCE_INLINE_ void set_rotation_scale_and_skew(real_t p_rot, const Size2 &p_scale, float p_skew); -	void rotate(real_t p_phi); +	void set_skew(const real_t p_angle); +	_FORCE_INLINE_ void set_rotation_and_scale(const real_t p_rot, const Size2 &p_scale); +	_FORCE_INLINE_ void set_rotation_scale_and_skew(const real_t p_rot, const Size2 &p_scale, const real_t p_skew); +	void rotate(const real_t p_phi);  	void scale(const Size2 &p_scale);  	void scale_basis(const Size2 &p_scale); -	void translate(real_t p_tx, real_t p_ty); +	void translate(const real_t p_tx, const real_t p_ty);  	void translate(const Vector2 &p_translation);  	real_t basis_determinant() const; @@ -92,7 +92,7 @@ struct Transform2D {  	Transform2D scaled(const Size2 &p_scale) const;  	Transform2D basis_scaled(const Size2 &p_scale) const;  	Transform2D translated(const Vector2 &p_offset) const; -	Transform2D rotated(real_t p_phi) const; +	Transform2D rotated(const real_t p_phi) const;  	Transform2D untranslated() const; @@ -110,7 +110,7 @@ struct Transform2D {  	void operator*=(const real_t p_val);  	Transform2D operator*(const real_t p_val) const; -	Transform2D interpolate_with(const Transform2D &p_transform, real_t p_c) const; +	Transform2D interpolate_with(const Transform2D &p_transform, const real_t p_c) const;  	_FORCE_INLINE_ Vector2 basis_xform(const Vector2 &p_vec) const;  	_FORCE_INLINE_ Vector2 basis_xform_inv(const Vector2 &p_vec) const; @@ -123,7 +123,7 @@ struct Transform2D {  	operator String() const; -	Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { +	Transform2D(const real_t xx, const real_t xy, const real_t yx, const real_t yy, const real_t ox, const real_t oy) {  		elements[0][0] = xx;  		elements[0][1] = xy;  		elements[1][0] = yx; @@ -138,7 +138,10 @@ struct Transform2D {  		elements[2] = p_origin;  	} -	Transform2D(real_t p_rot, const Vector2 &p_pos); +	Transform2D(const real_t p_rot, const Vector2 &p_pos); + +	Transform2D(const real_t p_rot, const Size2 &p_scale, const real_t p_skew, const Vector2 &p_pos); +  	Transform2D() {  		elements[0][0] = 1.0;  		elements[1][1] = 1.0; @@ -161,7 +164,7 @@ Vector2 Transform2D::xform(const Vector2 &p_vec) const {  	return Vector2(  				   tdotx(p_vec),  				   tdoty(p_vec)) + -		   elements[2]; +			elements[2];  }  Vector2 Transform2D::xform_inv(const Vector2 &p_vec) const { @@ -185,14 +188,14 @@ Rect2 Transform2D::xform(const Rect2 &p_rect) const {  	return new_rect;  } -void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) { +void Transform2D::set_rotation_and_scale(const real_t p_rot, const Size2 &p_scale) {  	elements[0][0] = Math::cos(p_rot) * p_scale.x;  	elements[1][1] = Math::cos(p_rot) * p_scale.y;  	elements[1][0] = -Math::sin(p_rot) * p_scale.y;  	elements[0][1] = Math::sin(p_rot) * p_scale.x;  } -void Transform2D::set_rotation_scale_and_skew(real_t p_rot, const Size2 &p_scale, float p_skew) { +void Transform2D::set_rotation_scale_and_skew(const real_t p_rot, const Size2 &p_scale, const real_t p_skew) {  	elements[0][0] = Math::cos(p_rot) * p_scale.x;  	elements[1][1] = Math::cos(p_rot + p_skew) * p_scale.y;  	elements[1][0] = -Math::sin(p_rot + p_skew) * p_scale.y; diff --git a/core/math/transform_3d.cpp b/core/math/transform_3d.cpp index 4f4943c8ef..78ef117443 100644 --- a/core/math/transform_3d.cpp +++ b/core/math/transform_3d.cpp @@ -175,9 +175,9 @@ Transform3D Transform3D::operator*(const real_t p_val) const {  Transform3D::operator String() const {  	return "[X: " + basis.get_axis(0).operator String() + -		   ", Y: " + basis.get_axis(1).operator String() + -		   ", Z: " + basis.get_axis(2).operator String() + -		   ", O: " + origin.operator String() + "]"; +			", Y: " + basis.get_axis(1).operator String() + +			", Z: " + basis.get_axis(2).operator String() + +			", O: " + origin.operator String() + "]";  }  Transform3D::Transform3D(const Basis &p_basis, const Vector3 &p_origin) : diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp index bf06c848c5..2f3da0b6a8 100644 --- a/core/math/triangle_mesh.cpp +++ b/core/math/triangle_mesh.cpp @@ -76,7 +76,7 @@ int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, in  	int index = r_max_alloc++;  	BVH *_new = &p_bvh[index];  	_new->aabb = aabb; -	_new->center = aabb.position + aabb.size * 0.5; +	_new->center = aabb.get_center();  	_new->face_index = -1;  	_new->left = left;  	_new->right = right; @@ -152,13 +152,13 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces) {  			bw[i].left = -1;  			bw[i].right = -1;  			bw[i].face_index = i; -			bw[i].center = bw[i].aabb.position + bw[i].aabb.size * 0.5; +			bw[i].center = bw[i].aabb.get_center();  		}  		vertices.resize(db.size());  		Vector3 *vw = vertices.ptrw(); -		for (Map<Vector3, int>::Element *E = db.front(); E; E = E->next()) { -			vw[E->get()] = E->key(); +		for (const KeyValue<Vector3, int> &E : db) { +			vw[E.value] = E.key;  		}  	} diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h index 463b0dd5c8..2d3b4db4bb 100644 --- a/core/math/triangle_mesh.h +++ b/core/math/triangle_mesh.h @@ -37,11 +37,13 @@  class TriangleMesh : public RefCounted {  	GDCLASS(TriangleMesh, RefCounted); +public:  	struct Triangle {  		Vector3 normal;  		int indices[3];  	}; +private:  	Vector<Triangle> triangles;  	Vector<Vector3> vertices; @@ -86,8 +88,8 @@ public:  	Vector3 get_area_normal(const AABB &p_aabb) const;  	Vector<Face3> get_faces() const; -	Vector<Triangle> get_triangles() const { return triangles; } -	Vector<Vector3> get_vertices() const { return vertices; } +	const Vector<Triangle> &get_triangles() const { return triangles; } +	const Vector<Vector3> &get_vertices() const { return vertices; }  	void get_indices(Vector<int> *r_triangles_indices) const;  	void create(const Vector<Vector3> &p_faces); diff --git a/core/math/triangulate.cpp b/core/math/triangulate.cpp index fa1588dbc5..28f1d96b14 100644 --- a/core/math/triangulate.cpp +++ b/core/math/triangulate.cpp @@ -42,18 +42,13 @@ real_t Triangulate::get_area(const Vector<Vector2> &contour) {  	return A * 0.5;  } -/* -     is_inside_triangle decides if a point P is Inside of the triangle -     defined by A, B, C. -   */ - +/* `is_inside_triangle` decides if a point P is inside the triangle + * defined by A, B, C. */  bool Triangulate::is_inside_triangle(real_t Ax, real_t Ay,  		real_t Bx, real_t By,  		real_t Cx, real_t Cy,  		real_t Px, real_t Py, -		bool include_edges) - -{ +		bool include_edges) {  	real_t ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;  	real_t cCROSSap, bCROSScp, aCROSSbp; diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index 54abc1b7f2..718e94eee4 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -34,6 +34,10 @@ real_t Vector2::angle() const {  	return Math::atan2(y, x);  } +Vector2 Vector2::from_angle(const real_t p_angle) { +	return Vector2(Math::cos(p_angle), Math::sin(p_angle)); +} +  real_t Vector2::length() const {  	return Math::sqrt(x * x + y * y);  } @@ -75,7 +79,7 @@ real_t Vector2::angle_to(const Vector2 &p_vector2) const {  }  real_t Vector2::angle_to_point(const Vector2 &p_vector2) const { -	return Math::atan2(y - p_vector2.y, x - p_vector2.x); +	return (p_vector2 - *this).angle();  }  real_t Vector2::dot(const Vector2 &p_other) const { @@ -87,7 +91,7 @@ real_t Vector2::cross(const Vector2 &p_other) const {  }  Vector2 Vector2::sign() const { -	return Vector2(SGN(x), SGN(y)); +	return Vector2(SIGN(x), SIGN(y));  }  Vector2 Vector2::floor() const { @@ -156,10 +160,11 @@ Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, c  	real_t t3 = t2 * t;  	Vector2 out; -	out = 0.5 * ((p1 * 2.0) + -						(-p0 + p2) * t + -						(2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 + -						(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); +	out = 0.5 * +			((p1 * 2.0) + +					(-p0 + p2) * t + +					(2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 + +					(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3);  	return out;  } diff --git a/core/math/vector2.h b/core/math/vector2.h index 330b4741b1..c0a189e040 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -70,12 +70,12 @@ struct Vector2 {  		x = y = p_value;  	} -	_FORCE_INLINE_ int min_axis() const { -		return x < y ? 0 : 1; +	_FORCE_INLINE_ Vector2::Axis min_axis_index() const { +		return x < y ? Vector2::AXIS_X : Vector2::AXIS_Y;  	} -	_FORCE_INLINE_ int max_axis() const { -		return x < y ? 1 : 0; +	_FORCE_INLINE_ Vector2::Axis max_axis_index() const { +		return x < y ? Vector2::AXIS_Y : Vector2::AXIS_X;  	}  	void normalize(); @@ -147,6 +147,7 @@ struct Vector2 {  	bool operator>=(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); }  	real_t angle() const; +	static Vector2 from_angle(const real_t p_angle);  	_FORCE_INLINE_ Vector2 abs() const {  		return Vector2(Math::abs(x), Math::abs(y)); @@ -300,12 +301,12 @@ struct Vector2i {  		return p_idx ? y : x;  	} -	_FORCE_INLINE_ int min_axis() const { -		return x < y ? 0 : 1; +	_FORCE_INLINE_ Vector2i::Axis min_axis_index() const { +		return x < y ? Vector2i::AXIS_X : Vector2i::AXIS_Y;  	} -	_FORCE_INLINE_ int max_axis() const { -		return x < y ? 1 : 0; +	_FORCE_INLINE_ Vector2i::Axis max_axis_index() const { +		return x < y ? Vector2i::AXIS_Y : Vector2i::AXIS_X;  	}  	Vector2i min(const Vector2i &p_vector2i) const { @@ -344,7 +345,7 @@ struct Vector2i {  	bool operator!=(const Vector2i &p_vec2) const;  	real_t aspect() const { return width / (real_t)height; } -	Vector2i sign() const { return Vector2i(SGN(x), SGN(y)); } +	Vector2i sign() const { return Vector2i(SIGN(x), SIGN(y)); }  	Vector2i abs() const { return Vector2i(ABS(x), ABS(y)); }  	Vector2i clamp(const Vector2i &p_min, const Vector2i &p_max) const; diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index 401c3ccd9c..b9bd04b8c1 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -93,10 +93,11 @@ Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, c  	real_t t3 = t2 * t;  	Vector3 out; -	out = 0.5 * ((p1 * 2.0) + -						(-p0 + p2) * t + -						(2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 + -						(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); +	out = 0.5 * +			((p1 * 2.0) + +					(-p0 + p2) * t + +					(2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 + +					(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3);  	return out;  } @@ -107,20 +108,14 @@ Vector3 Vector3::move_toward(const Vector3 &p_to, const real_t p_delta) const {  	return len <= p_delta || len < CMP_EPSILON ? p_to : v + vd / len * p_delta;  } -Basis Vector3::outer(const Vector3 &p_b) const { -	Vector3 row0(x * p_b.x, x * p_b.y, x * p_b.z); -	Vector3 row1(y * p_b.x, y * p_b.y, y * p_b.z); -	Vector3 row2(z * p_b.x, z * p_b.y, z * p_b.z); +Basis Vector3::outer(const Vector3 &p_with) const { +	Vector3 row0(x * p_with.x, x * p_with.y, x * p_with.z); +	Vector3 row1(y * p_with.x, y * p_with.y, y * p_with.z); +	Vector3 row2(z * p_with.x, z * p_with.y, z * p_with.z);  	return Basis(row0, row1, row2);  } -Basis Vector3::to_diagonal_matrix() const { -	return Basis(x, 0, 0, -			0, y, 0, -			0, 0, z); -} -  bool Vector3::is_equal_approx(const Vector3 &p_v) const {  	return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z);  } diff --git a/core/math/vector3.h b/core/math/vector3.h index 6a4c42f41b..c0f80e8f11 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -32,9 +32,9 @@  #define VECTOR3_H  #include "core/math/math_funcs.h" +#include "core/math/vector2.h"  #include "core/math/vector3i.h"  #include "core/string/ustring.h" -  class Basis;  struct Vector3 { @@ -71,12 +71,12 @@ struct Vector3 {  		x = y = z = p_value;  	} -	_FORCE_INLINE_ int min_axis() const { -		return x < y ? (x < z ? 0 : 2) : (y < z ? 1 : 2); +	_FORCE_INLINE_ Vector3::Axis min_axis_index() const { +		return x < y ? (x < z ? Vector3::AXIS_X : Vector3::AXIS_Z) : (y < z ? Vector3::AXIS_Y : Vector3::AXIS_Z);  	} -	_FORCE_INLINE_ int max_axis() const { -		return x < y ? (y < z ? 2 : 1) : (x < z ? 2 : 0); +	_FORCE_INLINE_ Vector3::Axis max_axis_index() const { +		return x < y ? (y < z ? Vector3::AXIS_Z : Vector3::AXIS_Y) : (x < z ? Vector3::AXIS_Z : Vector3::AXIS_X);  	}  	_FORCE_INLINE_ real_t length() const; @@ -103,10 +103,34 @@ struct Vector3 {  	Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const;  	Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const; -	_FORCE_INLINE_ Vector3 cross(const Vector3 &p_b) const; -	_FORCE_INLINE_ real_t dot(const Vector3 &p_b) const; -	Basis outer(const Vector3 &p_b) const; -	Basis to_diagonal_matrix() const; +	_FORCE_INLINE_ Vector2 octahedron_encode() const { +		Vector3 n = *this; +		n /= Math::abs(n.x) + Math::abs(n.y) + Math::abs(n.z); +		Vector2 o; +		if (n.z >= 0.0) { +			o.x = n.x; +			o.y = n.y; +		} else { +			o.x = (1.0 - Math::abs(n.y)) * (n.x >= 0.0 ? 1.0 : -1.0); +			o.y = (1.0 - Math::abs(n.x)) * (n.y >= 0.0 ? 1.0 : -1.0); +		} +		o.x = o.x * 0.5 + 0.5; +		o.y = o.y * 0.5 + 0.5; +		return o; +	} + +	static _FORCE_INLINE_ Vector3 octahedron_decode(const Vector2 &p_oct) { +		Vector2 f(p_oct.x * 2.0 - 1.0, p_oct.y * 2.0 - 1.0); +		Vector3 n(f.x, f.y, 1.0f - Math::abs(f.x) - Math::abs(f.y)); +		float t = CLAMP(-n.z, 0.0, 1.0); +		n.x += n.x >= 0 ? -t : t; +		n.y += n.y >= 0 ? -t : t; +		return n.normalized(); +	} + +	_FORCE_INLINE_ Vector3 cross(const Vector3 &p_with) const; +	_FORCE_INLINE_ real_t dot(const Vector3 &p_with) const; +	Basis outer(const Vector3 &p_with) const;  	_FORCE_INLINE_ Vector3 abs() const;  	_FORCE_INLINE_ Vector3 floor() const; @@ -175,17 +199,17 @@ struct Vector3 {  	}  }; -Vector3 Vector3::cross(const Vector3 &p_b) const { +Vector3 Vector3::cross(const Vector3 &p_with) const {  	Vector3 ret( -			(y * p_b.z) - (z * p_b.y), -			(z * p_b.x) - (x * p_b.z), -			(x * p_b.y) - (y * p_b.x)); +			(y * p_with.z) - (z * p_with.y), +			(z * p_with.x) - (x * p_with.z), +			(x * p_with.y) - (y * p_with.x));  	return ret;  } -real_t Vector3::dot(const Vector3 &p_b) const { -	return x * p_b.x + y * p_b.y + z * p_b.z; +real_t Vector3::dot(const Vector3 &p_with) const { +	return x * p_with.x + y * p_with.y + z * p_with.z;  }  Vector3 Vector3::abs() const { @@ -193,7 +217,7 @@ Vector3 Vector3::abs() const {  }  Vector3 Vector3::sign() const { -	return Vector3(SGN(x), SGN(y), SGN(z)); +	return Vector3(SIGN(x), SIGN(y), SIGN(z));  }  Vector3 Vector3::floor() const { diff --git a/core/math/vector3i.cpp b/core/math/vector3i.cpp index d3a57af77c..7812a0b41c 100644 --- a/core/math/vector3i.cpp +++ b/core/math/vector3i.cpp @@ -40,12 +40,12 @@ int32_t Vector3i::get_axis(const int p_axis) const {  	return operator[](p_axis);  } -int Vector3i::min_axis() const { -	return x < y ? (x < z ? 0 : 2) : (y < z ? 1 : 2); +Vector3i::Axis Vector3i::min_axis_index() const { +	return x < y ? (x < z ? Vector3i::AXIS_X : Vector3i::AXIS_Z) : (y < z ? Vector3i::AXIS_Y : Vector3i::AXIS_Z);  } -int Vector3i::max_axis() const { -	return x < y ? (y < z ? 2 : 1) : (x < z ? 2 : 0); +Vector3i::Axis Vector3i::max_axis_index() const { +	return x < y ? (y < z ? Vector3i::AXIS_Z : Vector3i::AXIS_Y) : (x < z ? Vector3i::AXIS_Z : Vector3i::AXIS_X);  }  Vector3i Vector3i::clamp(const Vector3i &p_min, const Vector3i &p_max) const { diff --git a/core/math/vector3i.h b/core/math/vector3i.h index 9308d09045..fba29a1f8d 100644 --- a/core/math/vector3i.h +++ b/core/math/vector3i.h @@ -62,8 +62,8 @@ struct Vector3i {  	void set_axis(const int p_axis, const int32_t p_value);  	int32_t get_axis(const int p_axis) const; -	int min_axis() const; -	int max_axis() const; +	Vector3i::Axis min_axis_index() const; +	Vector3i::Axis max_axis_index() const;  	_FORCE_INLINE_ void zero(); @@ -115,7 +115,7 @@ Vector3i Vector3i::abs() const {  }  Vector3i Vector3i::sign() const { -	return Vector3i(SGN(x), SGN(y), SGN(z)); +	return Vector3i(SIGN(x), SIGN(y), SIGN(z));  }  /* Operators */ diff --git a/core/multiplayer/SCsub b/core/multiplayer/SCsub new file mode 100644 index 0000000000..19a6549225 --- /dev/null +++ b/core/multiplayer/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import("env") + +env.add_source_files(env.core_sources, "*.cpp") diff --git a/core/io/multiplayer_peer.h b/core/multiplayer/multiplayer.h index 7ca4e7930b..be398f02c8 100644 --- a/core/io/multiplayer_peer.h +++ b/core/multiplayer/multiplayer.h @@ -1,5 +1,5 @@  /*************************************************************************/ -/*  multiplayer_peer.h                                                   */ +/*  multiplayer.h                                                        */  /*************************************************************************/  /*                       This file is part of:                           */  /*                           GODOT ENGINE                                */ @@ -28,58 +28,53 @@  /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */  /*************************************************************************/ -#ifndef NETWORKED_MULTIPLAYER_PEER_H -#define NETWORKED_MULTIPLAYER_PEER_H +#ifndef MULTIPLAYER_H +#define MULTIPLAYER_H -#include "core/io/packet_peer.h" +#include "core/variant/binder_common.h" -class MultiplayerPeer : public PacketPeer { -	GDCLASS(MultiplayerPeer, PacketPeer); +#include "core/string/string_name.h" -protected: -	static void _bind_methods(); +namespace Multiplayer { -public: -	enum { -		TARGET_PEER_BROADCAST = 0, -		TARGET_PEER_SERVER = 1 -	}; -	enum TransferMode { -		TRANSFER_MODE_UNRELIABLE, -		TRANSFER_MODE_UNRELIABLE_ORDERED, -		TRANSFER_MODE_RELIABLE, -	}; - -	enum ConnectionStatus { -		CONNECTION_DISCONNECTED, -		CONNECTION_CONNECTING, -		CONNECTION_CONNECTED, -	}; - -	virtual void set_transfer_channel(int p_channel) = 0; -	virtual int get_transfer_channel() const = 0; -	virtual void set_transfer_mode(TransferMode p_mode) = 0; -	virtual TransferMode get_transfer_mode() const = 0; -	virtual void set_target_peer(int p_peer_id) = 0; - -	virtual int get_packet_peer() const = 0; +enum TransferMode { +	TRANSFER_MODE_UNRELIABLE, +	TRANSFER_MODE_UNRELIABLE_ORDERED, +	TRANSFER_MODE_RELIABLE +}; -	virtual bool is_server() const = 0; +enum RPCMode { +	RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) +	RPC_MODE_ANY_PEER, // Any peer can call this RPC +	RPC_MODE_AUTHORITY, // / Only the node's multiplayer authority (server by default) can call this RPC +}; -	virtual void poll() = 0; +struct RPCConfig { +	StringName name; +	RPCMode rpc_mode = RPC_MODE_DISABLED; +	bool call_local = false; +	TransferMode transfer_mode = TRANSFER_MODE_RELIABLE; +	int channel = 0; -	virtual int get_unique_id() const = 0; +	bool operator==(RPCConfig const &p_other) const { +		return name == p_other.name; +	} +}; -	virtual void set_refuse_new_connections(bool p_enable) = 0; -	virtual bool is_refusing_new_connections() const = 0; +struct SortRPCConfig { +	StringName::AlphCompare compare; +	bool operator()(const RPCConfig &p_a, const RPCConfig &p_b) const { +		return compare(p_a.name, p_b.name); +	} +}; -	virtual ConnectionStatus get_connection_status() const = 0; -	uint32_t generate_unique_id() const; +}; // namespace Multiplayer -	MultiplayerPeer() {} -}; +// This is needed for proper docs generation (i.e. not "Multiplayer."-prefixed). +typedef Multiplayer::RPCMode RPCMode; +typedef Multiplayer::TransferMode TransferMode; -VARIANT_ENUM_CAST(MultiplayerPeer::TransferMode) -VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus) +VARIANT_ENUM_CAST(RPCMode); +VARIANT_ENUM_CAST(TransferMode); -#endif // NETWORKED_MULTIPLAYER_PEER_H +#endif // MULTIPLAYER_H diff --git a/core/multiplayer/multiplayer_api.cpp b/core/multiplayer/multiplayer_api.cpp new file mode 100644 index 0000000000..9543f77c1e --- /dev/null +++ b/core/multiplayer/multiplayer_api.cpp @@ -0,0 +1,668 @@ +/*************************************************************************/ +/*  multiplayer_api.cpp                                                  */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2021 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 "multiplayer_api.h" + +#include "core/debugger/engine_debugger.h" +#include "core/io/marshalls.h" +#include "core/multiplayer/multiplayer_replicator.h" +#include "core/multiplayer/rpc_manager.h" +#include "scene/main/node.h" + +#include <stdint.h> + +#ifdef DEBUG_ENABLED +#include "core/os/os.h" +#endif + +#ifdef DEBUG_ENABLED +void MultiplayerAPI::profile_bandwidth(const String &p_inout, int p_size) { +	if (EngineDebugger::is_profiling("multiplayer")) { +		Array values; +		values.push_back("bandwidth"); +		values.push_back(p_inout); +		values.push_back(OS::get_singleton()->get_ticks_msec()); +		values.push_back(p_size); +		EngineDebugger::profiler_add_frame_data("multiplayer", values); +	} +} +#endif + +void MultiplayerAPI::poll() { +	if (!multiplayer_peer.is_valid() || multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) { +		return; +	} + +	multiplayer_peer->poll(); + +	if (!multiplayer_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here. +		return; +	} + +	while (multiplayer_peer->get_available_packet_count()) { +		int sender = multiplayer_peer->get_packet_peer(); +		const uint8_t *packet; +		int len; + +		Error err = multiplayer_peer->get_packet(&packet, len); +		if (err != OK) { +			ERR_PRINT("Error getting packet!"); +			break; // Something is wrong! +		} + +		remote_sender_id = sender; +		_process_packet(sender, packet, len); +		remote_sender_id = 0; + +		if (!multiplayer_peer.is_valid()) { +			break; // It's also possible that a packet or RPC caused a disconnection, so also check here. +		} +	} +	if (multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTED) { +		replicator->poll(); +	} +} + +void MultiplayerAPI::clear() { +	replicator->clear(); +	connected_peers.clear(); +	path_get_cache.clear(); +	path_send_cache.clear(); +	packet_cache.clear(); +	last_send_cache_id = 1; +} + +void MultiplayerAPI::set_root_node(Node *p_node) { +	root_node = p_node; +} + +Node *MultiplayerAPI::get_root_node() { +	return root_node; +} + +void MultiplayerAPI::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) { +	if (p_peer == multiplayer_peer) { +		return; // Nothing to do +	} + +	ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, +			"Supplied MultiplayerPeer must be connecting or connected."); + +	if (multiplayer_peer.is_valid()) { +		multiplayer_peer->disconnect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); +		multiplayer_peer->disconnect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); +		multiplayer_peer->disconnect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); +		multiplayer_peer->disconnect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); +		multiplayer_peer->disconnect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); +		clear(); +	} + +	multiplayer_peer = p_peer; + +	if (multiplayer_peer.is_valid()) { +		multiplayer_peer->connect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); +		multiplayer_peer->connect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); +		multiplayer_peer->connect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); +		multiplayer_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); +		multiplayer_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); +	} +} + +Ref<MultiplayerPeer> MultiplayerAPI::get_multiplayer_peer() const { +	return multiplayer_peer; +} + +void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { +	ERR_FAIL_COND_MSG(root_node == nullptr, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it."); +	ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small."); + +#ifdef DEBUG_ENABLED +	profile_bandwidth("in", p_packet_len); +#endif + +	// Extract the `packet_type` from the LSB three bits: +	uint8_t packet_type = p_packet[0] & CMD_MASK; + +	switch (packet_type) { +		case NETWORK_COMMAND_SIMPLIFY_PATH: { +			_process_simplify_path(p_from, p_packet, p_packet_len); +		} break; + +		case NETWORK_COMMAND_CONFIRM_PATH: { +			_process_confirm_path(p_from, p_packet, p_packet_len); +		} break; + +		case NETWORK_COMMAND_REMOTE_CALL: { +			rpc_manager->process_rpc(p_from, p_packet, p_packet_len); +		} break; + +		case NETWORK_COMMAND_RAW: { +			_process_raw(p_from, p_packet, p_packet_len); +		} break; +		case NETWORK_COMMAND_SPAWN: { +			replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, true); +		} break; +		case NETWORK_COMMAND_DESPAWN: { +			replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, false); +		} break; +		case NETWORK_COMMAND_SYNC: { +			replicator->process_sync(p_from, p_packet, p_packet_len); +		} break; +	} +} + +void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { +	ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small."); +	int ofs = 1; + +	String methods_md5; +	methods_md5.parse_utf8((const char *)(p_packet + ofs), 32); +	ofs += 33; + +	int id = decode_uint32(&p_packet[ofs]); +	ofs += 4; + +	String paths; +	paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs); + +	NodePath path = paths; + +	if (!path_get_cache.has(p_from)) { +		path_get_cache[p_from] = PathGetCache(); +	} + +	Node *node = root_node->get_node(path); +	ERR_FAIL_COND(node == nullptr); +	const bool valid_rpc_checksum = rpc_manager->get_rpc_md5(node) == methods_md5; +	if (valid_rpc_checksum == false) { +		ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); +	} + +	PathGetCache::NodeInfo ni; +	ni.path = path; + +	path_get_cache[p_from].nodes[id] = ni; + +	// Encode path to send ack. +	CharString pname = String(path).utf8(); +	int len = encode_cstring(pname.get_data(), nullptr); + +	Vector<uint8_t> packet; + +	packet.resize(1 + 1 + len); +	packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH; +	packet.write[1] = valid_rpc_checksum; +	encode_cstring(pname.get_data(), &packet.write[2]); + +	multiplayer_peer->set_transfer_channel(0); +	multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); +	multiplayer_peer->set_target_peer(p_from); +	multiplayer_peer->put_packet(packet.ptr(), packet.size()); +} + +void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { +	ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small."); + +	const bool valid_rpc_checksum = p_packet[1]; + +	String paths; +	paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2); + +	NodePath path = paths; + +	if (valid_rpc_checksum == false) { +		ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); +	} + +	PathSentCache *psc = path_send_cache.getptr(path); +	ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache."); + +	Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from); +	ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path."); +	E->get() = true; +} + +bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) { +	bool has_all_peers = true; +	List<int> peers_to_add; // If one is missing, take note to add it. + +	for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { +		if (p_target < 0 && E->get() == -p_target) { +			continue; // Continue, excluded. +		} + +		if (p_target > 0 && E->get() != p_target) { +			continue; // Continue, not for this peer. +		} + +		Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); + +		if (!F || !F->get()) { +			// Path was not cached, or was cached but is unconfirmed. +			if (!F) { +				// Not cached at all, take note. +				peers_to_add.push_back(E->get()); +			} + +			has_all_peers = false; +		} +	} + +	if (peers_to_add.size() > 0) { +		// Those that need to be added, send a message for this. + +		// Encode function name. +		const CharString path = String(p_path).utf8(); +		const int path_len = encode_cstring(path.get_data(), nullptr); + +		// Extract MD5 from rpc methods list. +		const String methods_md5 = rpc_manager->get_rpc_md5(p_node); +		const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder. + +		Vector<uint8_t> packet; +		packet.resize(1 + 4 + path_len + methods_md5_len); +		int ofs = 0; + +		packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH; +		ofs += 1; + +		ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]); + +		ofs += encode_uint32(psc->id, &packet.write[ofs]); + +		ofs += encode_cstring(path.get_data(), &packet.write[ofs]); + +		for (int &E : peers_to_add) { +			multiplayer_peer->set_target_peer(E); // To all of you. +			multiplayer_peer->set_transfer_channel(0); +			multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); +			multiplayer_peer->put_packet(packet.ptr(), packet.size()); + +			psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed. +		} +	} + +	return has_all_peers; +} + +// The variant is compressed and encoded; The first byte contains all the meta +// information and the format is: +// - The first LSB 5 bits are used for the variant type. +// - The next two bits are used to store the encoding mode. +// - The most significant is used to store the boolean value. +#define VARIANT_META_TYPE_MASK 0x1F +#define VARIANT_META_EMODE_MASK 0x60 +#define VARIANT_META_BOOL_MASK 0x80 +#define ENCODE_8 0 << 5 +#define ENCODE_16 1 << 5 +#define ENCODE_32 2 << 5 +#define ENCODE_64 3 << 5 +Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) { +	// Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31 +	CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK); + +	uint8_t *buf = r_buffer; +	r_len = 0; +	uint8_t encode_mode = 0; + +	switch (p_variant.get_type()) { +		case Variant::BOOL: { +			if (buf) { +				// We still have 1 free bit in the meta, so let's use it. +				buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0; +				buf[0] |= encode_mode | p_variant.get_type(); +			} +			r_len += 1; +		} break; +		case Variant::INT: { +			if (buf) { +				// Reserve the first byte for the meta. +				buf += 1; +			} +			r_len += 1; +			int64_t val = p_variant; +			if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) { +				// Use 8 bit +				encode_mode = ENCODE_8; +				if (buf) { +					buf[0] = val; +				} +				r_len += 1; +			} else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) { +				// Use 16 bit +				encode_mode = ENCODE_16; +				if (buf) { +					encode_uint16(val, buf); +				} +				r_len += 2; +			} else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) { +				// Use 32 bit +				encode_mode = ENCODE_32; +				if (buf) { +					encode_uint32(val, buf); +				} +				r_len += 4; +			} else { +				// Use 64 bit +				encode_mode = ENCODE_64; +				if (buf) { +					encode_uint64(val, buf); +				} +				r_len += 8; +			} +			// Store the meta +			if (buf) { +				buf -= 1; +				buf[0] = encode_mode | p_variant.get_type(); +			} +		} break; +		default: +			// Any other case is not yet compressed. +			Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding); +			if (err != OK) { +				return err; +			} +			if (r_buffer) { +				// The first byte is not used by the marshalling, so store the type +				// so we know how to decompress and decode this variant. +				r_buffer[0] = p_variant.get_type(); +			} +	} + +	return OK; +} + +Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) { +	const uint8_t *buf = p_buffer; +	int len = p_len; + +	ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); +	uint8_t type = buf[0] & VARIANT_META_TYPE_MASK; +	uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK; + +	ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA); + +	switch (type) { +		case Variant::BOOL: { +			bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0; +			r_variant = val; +			if (r_len) { +				*r_len = 1; +			} +		} break; +		case Variant::INT: { +			buf += 1; +			len -= 1; +			if (r_len) { +				*r_len = 1; +			} +			if (encode_mode == ENCODE_8) { +				// 8 bits. +				ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); +				int8_t val = buf[0]; +				r_variant = val; +				if (r_len) { +					(*r_len) += 1; +				} +			} else if (encode_mode == ENCODE_16) { +				// 16 bits. +				ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA); +				int16_t val = decode_uint16(buf); +				r_variant = val; +				if (r_len) { +					(*r_len) += 2; +				} +			} else if (encode_mode == ENCODE_32) { +				// 32 bits. +				ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); +				int32_t val = decode_uint32(buf); +				r_variant = val; +				if (r_len) { +					(*r_len) += 4; +				} +			} else { +				// 64 bits. +				ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); +				int64_t val = decode_uint64(buf); +				r_variant = val; +				if (r_len) { +					(*r_len) += 8; +				} +			} +		} break; +		default: +			Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding); +			if (err != OK) { +				return err; +			} +	} + +	return OK; +} + +void MultiplayerAPI::_add_peer(int p_id) { +	connected_peers.insert(p_id); +	path_get_cache.insert(p_id, PathGetCache()); +	if (is_server()) { +		replicator->spawn_all(p_id); +	} +	emit_signal(SNAME("peer_connected"), p_id); +} + +void MultiplayerAPI::_del_peer(int p_id) { +	connected_peers.erase(p_id); +	// Cleanup get cache. +	path_get_cache.erase(p_id); +	// Cleanup sent cache. +	// Some refactoring is needed to make this faster and do paths GC. +	List<NodePath> keys; +	path_send_cache.get_key_list(&keys); +	for (const NodePath &E : keys) { +		PathSentCache *psc = path_send_cache.getptr(E); +		psc->confirmed_peers.erase(p_id); +	} +	emit_signal(SNAME("peer_disconnected"), p_id); +} + +void MultiplayerAPI::_connected_to_server() { +	emit_signal(SNAME("connected_to_server")); +} + +void MultiplayerAPI::_connection_failed() { +	emit_signal(SNAME("connection_failed")); +} + +void MultiplayerAPI::_server_disconnected() { +	emit_signal(SNAME("server_disconnected")); +} + +Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, Multiplayer::TransferMode p_mode, int p_channel) { +	ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet."); +	ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no multiplayer peer is active."); +	ERR_FAIL_COND_V_MSG(multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a multiplayer peer which is not connected."); + +	if (packet_cache.size() < p_data.size() + 1) { +		packet_cache.resize(p_data.size() + 1); +	} + +	const uint8_t *r = p_data.ptr(); +	packet_cache.write[0] = NETWORK_COMMAND_RAW; +	memcpy(&packet_cache.write[1], &r[0], p_data.size()); + +	multiplayer_peer->set_target_peer(p_to); +	multiplayer_peer->set_transfer_channel(p_channel); +	multiplayer_peer->set_transfer_mode(p_mode); + +	return multiplayer_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); +} + +void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { +	ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); + +	Vector<uint8_t> out; +	int len = p_packet_len - 1; +	out.resize(len); +	{ +		uint8_t *w = out.ptrw(); +		memcpy(&w[0], &p_packet[1], len); +	} +	emit_signal(SNAME("peer_packet"), p_from, out); +} + +bool MultiplayerAPI::is_cache_confirmed(NodePath p_path, int p_peer) { +	const PathSentCache *psc = path_send_cache.getptr(p_path); +	ERR_FAIL_COND_V(!psc, false); +	const Map<int, bool>::Element *F = psc->confirmed_peers.find(p_peer); +	ERR_FAIL_COND_V(!F, false); // Should never happen. +	return F->get(); +} + +bool MultiplayerAPI::send_confirm_path(Node *p_node, NodePath p_path, int p_peer_id, int &r_id) { +	// See if the path is cached. +	PathSentCache *psc = path_send_cache.getptr(p_path); +	if (!psc) { +		// Path is not cached, create. +		path_send_cache[p_path] = PathSentCache(); +		psc = path_send_cache.getptr(p_path); +		psc->id = last_send_cache_id++; +	} +	r_id = psc->id; + +	// See if all peers have cached path (if so, call can be fast). +	return _send_confirm_path(p_node, p_path, psc, p_peer_id); +} + +Node *MultiplayerAPI::get_cached_node(int p_from, uint32_t p_node_id) { +	Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); +	ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from)); + +	Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(p_node_id); +	ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_node_id, p_from)); + +	PathGetCache::NodeInfo *ni = &F->get(); +	Node *node = root_node->get_node(ni->path); +	if (!node) { +		ERR_PRINT("Failed to get cached path: " + String(ni->path) + "."); +	} +	return node; +} + +int MultiplayerAPI::get_unique_id() const { +	ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), 0, "No multiplayer peer is assigned. Unable to get unique ID."); +	return multiplayer_peer->get_unique_id(); +} + +bool MultiplayerAPI::is_server() const { +	return multiplayer_peer.is_valid() && multiplayer_peer->is_server(); +} + +void MultiplayerAPI::set_refuse_new_connections(bool p_refuse) { +	ERR_FAIL_COND_MSG(!multiplayer_peer.is_valid(), "No multiplayer peer is assigned. Unable to set 'refuse_new_connections'."); +	multiplayer_peer->set_refuse_new_connections(p_refuse); +} + +bool MultiplayerAPI::is_refusing_new_connections() const { +	ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), false, "No multiplayer peer is assigned. Unable to get 'refuse_new_connections'."); +	return multiplayer_peer->is_refusing_new_connections(); +} + +Vector<int> MultiplayerAPI::get_peer_ids() const { +	ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), Vector<int>(), "No multiplayer peer is assigned. Assume no peers are connected."); + +	Vector<int> ret; +	for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { +		ret.push_back(E->get()); +	} + +	return ret; +} + +void MultiplayerAPI::set_allow_object_decoding(bool p_enable) { +	allow_object_decoding = p_enable; +} + +bool MultiplayerAPI::is_object_decoding_allowed() const { +	return allow_object_decoding; +} + +void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) { +	replicator->scene_enter_exit_notify(p_scene, p_node, p_enter); +} + +void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { +	rpc_manager->rpcp(p_node, p_peer_id, p_method, p_arg, p_argcount); +} + +void MultiplayerAPI::_bind_methods() { +	ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); +	ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node); +	ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); +	ClassDB::bind_method(D_METHOD("has_multiplayer_peer"), &MultiplayerAPI::has_multiplayer_peer); +	ClassDB::bind_method(D_METHOD("get_multiplayer_peer"), &MultiplayerAPI::get_multiplayer_peer); +	ClassDB::bind_method(D_METHOD("set_multiplayer_peer", "peer"), &MultiplayerAPI::set_multiplayer_peer); +	ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerAPI::get_unique_id); +	ClassDB::bind_method(D_METHOD("is_server"), &MultiplayerAPI::is_server); +	ClassDB::bind_method(D_METHOD("get_remote_sender_id"), &MultiplayerAPI::get_remote_sender_id); +	ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll); +	ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear); + +	ClassDB::bind_method(D_METHOD("get_peers"), &MultiplayerAPI::get_peer_ids); +	ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "refuse"), &MultiplayerAPI::set_refuse_new_connections); +	ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerAPI::is_refusing_new_connections); +	ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding); +	ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed); +	ClassDB::bind_method(D_METHOD("get_replicator"), &MultiplayerAPI::get_replicator); + +	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); +	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections"); +	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer"); +	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node"); +	ADD_PROPERTY_DEFAULT("refuse_new_connections", false); +	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replicator", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerReplicator", PROPERTY_USAGE_NONE), "", "get_replicator"); + +	ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id"))); +	ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id"))); +	ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet"))); +	ADD_SIGNAL(MethodInfo("connected_to_server")); +	ADD_SIGNAL(MethodInfo("connection_failed")); +	ADD_SIGNAL(MethodInfo("server_disconnected")); +} + +MultiplayerAPI::MultiplayerAPI() { +	replicator = memnew(MultiplayerReplicator(this)); +	rpc_manager = memnew(RPCManager(this)); +	clear(); +} + +MultiplayerAPI::~MultiplayerAPI() { +	clear(); +	memdelete(replicator); +	memdelete(rpc_manager); +} diff --git a/core/io/multiplayer_api.h b/core/multiplayer/multiplayer_api.h index 3c96a3eed1..1fb0318403 100644 --- a/core/io/multiplayer_api.h +++ b/core/multiplayer/multiplayer_api.h @@ -31,41 +31,17 @@  #ifndef MULTIPLAYER_API_H  #define MULTIPLAYER_API_H -#include "core/io/multiplayer_peer.h" -#include "core/io/resource_uid.h" +#include "core/multiplayer/multiplayer.h" +#include "core/multiplayer/multiplayer_peer.h"  #include "core/object/ref_counted.h"  class MultiplayerReplicator; +class RPCManager;  class MultiplayerAPI : public RefCounted {  	GDCLASS(MultiplayerAPI, RefCounted);  public: -	enum RPCMode { -		RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) -		RPC_MODE_ANY, // Any peer can call this rpc() -		RPC_MODE_AUTHORITY, // Only the node's network authority (server by default) can call this rpc() -	}; - -	struct RPCConfig { -		StringName name; -		RPCMode rpc_mode = RPC_MODE_DISABLED; -		bool sync = false; -		MultiplayerPeer::TransferMode transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE; -		int channel = 0; - -		bool operator==(RPCConfig const &p_other) const { -			return name == p_other.name; -		} -	}; - -	struct SortRPCConfig { -		StringName::AlphCompare compare; -		bool operator()(const RPCConfig &p_a, const RPCConfig &p_b) const { -			return compare(p_a.name, p_b.name); -		} -	}; -  	enum NetworkCommands {  		NETWORK_COMMAND_REMOTE_CALL = 0,  		NETWORK_COMMAND_SIMPLIFY_PATH, @@ -73,24 +49,20 @@ public:  		NETWORK_COMMAND_RAW,  		NETWORK_COMMAND_SPAWN,  		NETWORK_COMMAND_DESPAWN, -		NETWORK_COMMAND_SYNC, // This is the max we can have. We should optmize simplify/confirm, possibly spawn/despawn. +		NETWORK_COMMAND_SYNC,  	}; -	enum NetworkNodeIdCompression { -		NETWORK_NODE_ID_COMPRESSION_8 = 0, -		NETWORK_NODE_ID_COMPRESSION_16, -		NETWORK_NODE_ID_COMPRESSION_32, -	}; - -	enum NetworkNameIdCompression { -		NETWORK_NAME_ID_COMPRESSION_8 = 0, -		NETWORK_NAME_ID_COMPRESSION_16, +	// For each command, the 4 MSB can contain custom flags, as defined by subsystems. +	enum { +		CMD_FLAG_0_SHIFT = 4, +		CMD_FLAG_1_SHIFT = 5, +		CMD_FLAG_2_SHIFT = 6, +		CMD_FLAG_3_SHIFT = 7,  	}; +	// This is the mask that will be used to extract the command.  	enum { -		NODE_ID_COMPRESSION_SHIFT = 3, -		NAME_ID_COMPRESSION_SHIFT = 5, -		BYTE_ONLY_OR_NO_ARGS_SHIFT = 6, +		CMD_MASK = 7, // 0x7 -> 0b00001111  	};  private: @@ -110,49 +82,52 @@ private:  		Map<int, NodeInfo> nodes;  	}; -	Ref<MultiplayerPeer> network_peer; -	int rpc_sender_id = 0; +	Ref<MultiplayerPeer> multiplayer_peer;  	Set<int> connected_peers; +	int remote_sender_id = 0; +	int remote_sender_override = 0; +  	HashMap<NodePath, PathSentCache> path_send_cache;  	Map<int, PathGetCache> path_get_cache;  	int last_send_cache_id;  	Vector<uint8_t> packet_cache; +  	Node *root_node = nullptr;  	bool allow_object_decoding = false; +  	MultiplayerReplicator *replicator = nullptr; +	RPCManager *rpc_manager = nullptr;  protected:  	static void _bind_methods(); +	bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target);  	void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);  	void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len);  	void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len); -	Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len); -	void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);  	void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len); -	void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount); -	bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target); -  public:  	void poll();  	void clear();  	void set_root_node(Node *p_node);  	Node *get_root_node(); -	void set_network_peer(const Ref<MultiplayerPeer> &p_peer); -	Ref<MultiplayerPeer> get_network_peer() const; -	Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, MultiplayerPeer::TransferMode p_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE, int p_channel = 0); +	void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer); +	Ref<MultiplayerPeer> get_multiplayer_peer() const; + +	Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, Multiplayer::TransferMode p_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0);  	Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len);  	Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len);  	// Called by Node.rpc -	void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount); +	void rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);  	// Called by Node._notification  	void scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter);  	// Called by replicator  	bool send_confirm_path(Node *p_node, NodePath p_path, int p_target, int &p_id);  	Node *get_cached_node(int p_from, uint32_t p_node_id); +	bool is_cache_confirmed(NodePath p_path, int p_peer);  	void _add_peer(int p_id);  	void _del_peer(int p_id); @@ -160,23 +135,28 @@ public:  	void _connection_failed();  	void _server_disconnected(); -	bool has_network_peer() const { return network_peer.is_valid(); } -	Vector<int> get_network_connected_peers() const; -	int get_rpc_sender_id() const { return rpc_sender_id; } -	int get_network_unique_id() const; -	bool is_network_server() const; -	void set_refuse_new_network_connections(bool p_refuse); -	bool is_refusing_new_network_connections() const; +	bool has_multiplayer_peer() const { return multiplayer_peer.is_valid(); } +	Vector<int> get_peer_ids() const; +	const Set<int> get_connected_peers() const { return connected_peers; } +	int get_remote_sender_id() const { return remote_sender_override ? remote_sender_override : remote_sender_id; } +	void set_remote_sender_override(int p_id) { remote_sender_override = p_id; } +	int get_unique_id() const; +	bool is_server() const; +	void set_refuse_new_connections(bool p_refuse); +	bool is_refusing_new_connections() const;  	void set_allow_object_decoding(bool p_enable);  	bool is_object_decoding_allowed() const; -	MultiplayerReplicator *get_replicator() const; +	MultiplayerReplicator *get_replicator() const { return replicator; } +	RPCManager *get_rpc_manager() const { return rpc_manager; } + +#ifdef DEBUG_ENABLED +	void profile_bandwidth(const String &p_inout, int p_size); +#endif  	MultiplayerAPI();  	~MultiplayerAPI();  }; -VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode); -  #endif // MULTIPLAYER_API_H diff --git a/core/io/multiplayer_peer.cpp b/core/multiplayer/multiplayer_peer.cpp index 83cf24d7e3..3c33948e2f 100644 --- a/core/io/multiplayer_peer.cpp +++ b/core/multiplayer/multiplayer_peer.cpp @@ -53,6 +53,30 @@ uint32_t MultiplayerPeer::generate_unique_id() const {  	return hash;  } +void MultiplayerPeer::set_transfer_channel(int p_channel) { +	transfer_channel = p_channel; +} + +int MultiplayerPeer::get_transfer_channel() const { +	return transfer_channel; +} + +void MultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) { +	transfer_mode = p_mode; +} + +Multiplayer::TransferMode MultiplayerPeer::get_transfer_mode() const { +	return transfer_mode; +} + +void MultiplayerPeer::set_refuse_new_connections(bool p_enable) { +	refuse_connections = p_enable; +} + +bool MultiplayerPeer::is_refusing_new_connections() const { +	return refuse_connections; +} +  void MultiplayerPeer::_bind_methods() {  	ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel);  	ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel); @@ -75,10 +99,6 @@ void MultiplayerPeer::_bind_methods() {  	ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode");  	ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel"); -	BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE); -	BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED); -	BIND_ENUM_CONSTANT(TRANSFER_MODE_RELIABLE); -  	BIND_ENUM_CONSTANT(CONNECTION_DISCONNECTED);  	BIND_ENUM_CONSTANT(CONNECTION_CONNECTING);  	BIND_ENUM_CONSTANT(CONNECTION_CONNECTED); @@ -92,3 +112,160 @@ void MultiplayerPeer::_bind_methods() {  	ADD_SIGNAL(MethodInfo("connection_succeeded"));  	ADD_SIGNAL(MethodInfo("connection_failed"));  } + +/*************/ + +int MultiplayerPeerExtension::get_available_packet_count() const { +	int count; +	if (GDVIRTUAL_CALL(_get_available_packet_count, count)) { +		return count; +	} +	WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_available_packet_count is unimplemented!"); +	return -1; +} + +Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { +	int err; +	if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { +		return (Error)err; +	} +	WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_native is unimplemented!"); +	return FAILED; +} + +Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { +	int err; +	if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { +		return (Error)err; +	} +	WARN_PRINT_ONCE("MultiplayerPeerExtension::_put_packet_native is unimplemented!"); +	return FAILED; +} + +int MultiplayerPeerExtension::get_max_packet_size() const { +	int size; +	if (GDVIRTUAL_CALL(_get_max_packet_size, size)) { +		return size; +	} +	WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_max_packet_size is unimplemented!"); +	return 0; +} + +void MultiplayerPeerExtension::set_transfer_channel(int p_channel) { +	if (GDVIRTUAL_CALL(_set_transfer_channel, p_channel)) { +		return; +	} +	MultiplayerPeer::set_transfer_channel(p_channel); +} + +int MultiplayerPeerExtension::get_transfer_channel() const { +	int channel; +	if (GDVIRTUAL_CALL(_get_transfer_channel, channel)) { +		return channel; +	} +	return MultiplayerPeer::get_transfer_channel(); +} + +void MultiplayerPeerExtension::set_transfer_mode(Multiplayer::TransferMode p_mode) { +	if (GDVIRTUAL_CALL(_set_transfer_mode, p_mode)) { +		return; +	} +	MultiplayerPeer::set_transfer_mode(p_mode); +} + +Multiplayer::TransferMode MultiplayerPeerExtension::get_transfer_mode() const { +	int mode; +	if (GDVIRTUAL_CALL(_get_transfer_mode, mode)) { +		return (Multiplayer::TransferMode)mode; +	} +	return MultiplayerPeer::get_transfer_mode(); +} + +void MultiplayerPeerExtension::set_target_peer(int p_peer_id) { +	if (GDVIRTUAL_CALL(_set_target_peer, p_peer_id)) { +		return; +	} +	WARN_PRINT_ONCE("MultiplayerPeerExtension::_set_target_peer is unimplemented!"); +} + +int MultiplayerPeerExtension::get_packet_peer() const { +	int peer; +	if (GDVIRTUAL_CALL(_get_packet_peer, peer)) { +		return peer; +	} +	WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_peer is unimplemented!"); +	return 0; +} + +bool MultiplayerPeerExtension::is_server() const { +	bool server; +	if (GDVIRTUAL_CALL(_is_server, server)) { +		return server; +	} +	WARN_PRINT_ONCE("MultiplayerPeerExtension::_is_server is unimplemented!"); +	return false; +} + +void MultiplayerPeerExtension::poll() { +	int err; +	if (GDVIRTUAL_CALL(_poll, err)) { +		return; +	} +	WARN_PRINT_ONCE("MultiplayerPeerExtension::_poll is unimplemented!"); +} + +int MultiplayerPeerExtension::get_unique_id() const { +	int id; +	if (GDVIRTUAL_CALL(_get_unique_id, id)) { +		return id; +	} +	WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_unique_id is unimplemented!"); +	return 0; +} + +void MultiplayerPeerExtension::set_refuse_new_connections(bool p_enable) { +	if (GDVIRTUAL_CALL(_set_refuse_new_connections, p_enable)) { +		return; +	} +	MultiplayerPeer::set_refuse_new_connections(p_enable); +} + +bool MultiplayerPeerExtension::is_refusing_new_connections() const { +	bool refusing; +	if (GDVIRTUAL_CALL(_is_refusing_new_connections, refusing)) { +		return refusing; +	} +	return MultiplayerPeer::is_refusing_new_connections(); +} + +MultiplayerPeer::ConnectionStatus MultiplayerPeerExtension::get_connection_status() const { +	int status; +	if (GDVIRTUAL_CALL(_get_connection_status, status)) { +		return (ConnectionStatus)status; +	} +	WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_connection_status is unimplemented!"); +	return CONNECTION_DISCONNECTED; +} + +void MultiplayerPeerExtension::_bind_methods() { +	GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); +	GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); +	GDVIRTUAL_BIND(_get_available_packet_count); +	GDVIRTUAL_BIND(_get_max_packet_size); + +	GDVIRTUAL_BIND(_set_transfer_channel, "p_channel"); +	GDVIRTUAL_BIND(_get_transfer_channel); + +	GDVIRTUAL_BIND(_set_transfer_mode, "p_mode"); +	GDVIRTUAL_BIND(_get_transfer_mode); + +	GDVIRTUAL_BIND(_set_target_peer, "p_peer"); + +	GDVIRTUAL_BIND(_get_packet_peer); +	GDVIRTUAL_BIND(_is_server); +	GDVIRTUAL_BIND(_poll); +	GDVIRTUAL_BIND(_get_unique_id); +	GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable"); +	GDVIRTUAL_BIND(_is_refusing_new_connections); +	GDVIRTUAL_BIND(_get_connection_status); +} diff --git a/core/multiplayer/multiplayer_peer.h b/core/multiplayer/multiplayer_peer.h new file mode 100644 index 0000000000..126ba9e645 --- /dev/null +++ b/core/multiplayer/multiplayer_peer.h @@ -0,0 +1,144 @@ +/*************************************************************************/ +/*  multiplayer_peer.h                                                   */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2021 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 NETWORKED_MULTIPLAYER_PEER_H +#define NETWORKED_MULTIPLAYER_PEER_H + +#include "core/io/packet_peer.h" +#include "core/multiplayer/multiplayer.h" + +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" + +class MultiplayerPeer : public PacketPeer { +	GDCLASS(MultiplayerPeer, PacketPeer); + +protected: +	static void _bind_methods(); + +private: +	int transfer_channel = 0; +	Multiplayer::TransferMode transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; +	bool refuse_connections = false; + +public: +	enum { +		TARGET_PEER_BROADCAST = 0, +		TARGET_PEER_SERVER = 1 +	}; + +	enum ConnectionStatus { +		CONNECTION_DISCONNECTED, +		CONNECTION_CONNECTING, +		CONNECTION_CONNECTED, +	}; + +	virtual void set_transfer_channel(int p_channel); +	virtual int get_transfer_channel() const; +	virtual void set_transfer_mode(Multiplayer::TransferMode p_mode); +	virtual Multiplayer::TransferMode get_transfer_mode() const; +	virtual void set_refuse_new_connections(bool p_enable); +	virtual bool is_refusing_new_connections() const; + +	virtual void set_target_peer(int p_peer_id) = 0; + +	virtual int get_packet_peer() const = 0; + +	virtual bool is_server() const = 0; + +	virtual void poll() = 0; + +	virtual int get_unique_id() const = 0; + +	virtual ConnectionStatus get_connection_status() const = 0; + +	uint32_t generate_unique_id() const; + +	MultiplayerPeer() {} +}; + +VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus); + +class MultiplayerPeerExtension : public MultiplayerPeer { +	GDCLASS(MultiplayerPeerExtension, MultiplayerPeer); + +protected: +	static void _bind_methods(); + +public: +	/* PacketPeer */ +	virtual int get_available_packet_count() const override; +	virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet +	virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; +	virtual int get_max_packet_size() const override; + +	/* MultiplayerPeer */ +	virtual void set_transfer_channel(int p_channel) override; +	virtual int get_transfer_channel() const override; +	virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) override; +	virtual Multiplayer::TransferMode get_transfer_mode() const override; +	virtual void set_target_peer(int p_peer_id) override; + +	virtual int get_packet_peer() const override; + +	virtual bool is_server() const override; + +	virtual void poll() override; + +	virtual int get_unique_id() const override; + +	virtual void set_refuse_new_connections(bool p_enable) override; +	virtual bool is_refusing_new_connections() const override; + +	virtual ConnectionStatus get_connection_status() const override; + +	/* PacketPeer GDExtension */ +	GDVIRTUAL0RC(int, _get_available_packet_count); +	GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); +	GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int); +	GDVIRTUAL0RC(int, _get_max_packet_size); + +	/* MultiplayerPeer GDExtension */ +	GDVIRTUAL1(_set_transfer_channel, int); +	GDVIRTUAL0RC(int, _get_transfer_channel); +	GDVIRTUAL1(_set_transfer_mode, int); +	GDVIRTUAL0RC(int, _get_transfer_mode); +	GDVIRTUAL1(_set_target_peer, int); +	GDVIRTUAL0RC(int, _get_packet_peer); +	GDVIRTUAL0RC(bool, _is_server); +	GDVIRTUAL0R(int, _poll); +	GDVIRTUAL0RC(int, _get_unique_id); +	GDVIRTUAL1(_set_refuse_new_connections, bool); +	GDVIRTUAL0RC(bool, _is_refusing_new_connections); +	GDVIRTUAL0RC(int, _get_connection_status); +}; + +#endif // NETWORKED_MULTIPLAYER_PEER_H diff --git a/core/io/multiplayer_replicator.cpp b/core/multiplayer/multiplayer_replicator.cpp index 1642aab136..c57562552a 100644 --- a/core/io/multiplayer_replicator.cpp +++ b/core/multiplayer/multiplayer_replicator.cpp @@ -28,7 +28,7 @@  /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */  /*************************************************************************/ -#include "core/io/multiplayer_replicator.h" +#include "core/multiplayer/multiplayer_replicator.h"  #include "core/io/marshalls.h"  #include "scene/main/node.h" @@ -88,7 +88,7 @@ Error MultiplayerReplicator::_sync_all_default(const ResourceUID::ID &p_scene_id  	}  	int ofs = 0;  	uint8_t *ptr = packet_cache.ptrw(); -	ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC + ((same_size ? 1 : 0) << MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT); +	ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC | (same_size ? BYTE_OR_ZERO_FLAG : 0);  	ofs = 1;  	ofs += encode_uint64(p_scene_id, &ptr[ofs]);  	ptr[ofs] = cfg.sync_recv++; @@ -113,19 +113,19 @@ Error MultiplayerReplicator::_sync_all_default(const ResourceUID::ID &p_scene_id  		ERR_CONTINUE(err);  		ofs += size;  	} -	Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer(); -	network_peer->set_target_peer(p_peer); -	network_peer->set_transfer_channel(0); -	network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_UNRELIABLE); -	return network_peer->put_packet(ptr, ofs); +	Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); +	peer->set_target_peer(p_peer); +	peer->set_transfer_channel(0); +	peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_UNRELIABLE); +	return peer->put_packet(ptr, ofs);  }  void MultiplayerReplicator::_process_default_sync(const ResourceUID::ID &p_id, const uint8_t *p_packet, int p_packet_len) {  	ERR_FAIL_COND_MSG(p_packet_len < SYNC_CMD_OFFSET + 5, "Invalid spawn packet received");  	ERR_FAIL_COND_MSG(!replications.has(p_id), "Invalid spawn ID received " + itos(p_id));  	SceneConfig &cfg = replications[p_id]; -	ERR_FAIL_COND_MSG(cfg.mode != REPLICATION_MODE_SERVER || multiplayer->is_network_server(), "The defualt implementation only allows sync packets from the server"); -	const bool same_size = ((p_packet[0] & 64) >> MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1; +	ERR_FAIL_COND_MSG(cfg.mode != REPLICATION_MODE_SERVER || multiplayer->is_server(), "The defualt implementation only allows sync packets from the server"); +	const bool same_size = p_packet[0] & BYTE_OR_ZERO_FLAG;  	int ofs = SYNC_CMD_OFFSET;  	int time = p_packet[ofs];  	// Skip old update. @@ -189,6 +189,8 @@ Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const Re  	bool is_raw = false;  	if (state_variants.size() == 1 && state_variants[0].get_type() == Variant::PACKED_BYTE_ARRAY) {  		is_raw = true; +		const PackedByteArray pba = state_variants[0]; +		state_len = pba.size();  	} else if (state_variants.size()) {  		err = _encode_state(state_variants, nullptr, state_len);  		ERR_FAIL_COND_V(err, err); @@ -205,7 +207,7 @@ Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const Re  	const Vector<StringName> names = rel_path.get_names();  	ERR_FAIL_COND_V(names.size() < 2, ERR_INVALID_PARAMETER); -	NodePath parent = NodePath(names.subarray(0, names.size() - 2), false); +	NodePath parent = NodePath(names.slice(0, names.size() - 1), false);  	ERR_FAIL_COND_V_MSG(!root_node->has_node(parent), ERR_INVALID_PARAMETER, "Path not found: " + parent);  	int path_id = 0; @@ -216,7 +218,7 @@ Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const Re  	int nlen = encode_cstring(cname.get_data(), nullptr);  	MAKE_ROOM(SPAWN_CMD_OFFSET + 4 + 4 + nlen + state_len);  	uint8_t *ptr = packet_cache.ptrw(); -	ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) + ((is_raw ? 1 : 0) << MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT); +	ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) | (is_raw ? BYTE_OR_ZERO_FLAG : 0);  	ofs = 1;  	ofs += encode_uint64(p_scene_id, &ptr[ofs]);  	ofs += encode_uint32(path_id, &ptr[ofs]); @@ -231,11 +233,11 @@ Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const Re  		memcpy(&ptr[ofs], pba.ptr(), state_len);  	} -	Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer(); -	network_peer->set_target_peer(p_peer_id); -	network_peer->set_transfer_channel(0); -	network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); -	return network_peer->put_packet(ptr, ofs + state_len); +	Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); +	peer->set_target_peer(p_peer_id); +	peer->set_transfer_channel(0); +	peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); +	return peer->put_packet(ptr, ofs + state_len);  }  void MultiplayerReplicator::_process_default_spawn_despawn(int p_from, const ResourceUID::ID &p_scene_id, const uint8_t *p_packet, int p_packet_len, bool p_spawn) { @@ -260,7 +262,7 @@ void MultiplayerReplicator::_process_default_spawn_despawn(int p_from, const Res  	if (cfg.mode == REPLICATION_MODE_SERVER && p_from == 1) {  		String scene_path = ResourceUID::get_singleton()->get_id_path(p_scene_id);  		if (p_spawn) { -			const bool is_raw = ((p_packet[0] & 64) >> MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1; +			const bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1;  			ERR_FAIL_COND_MSG(parent->has_node(name), vformat("Unable to spawn node. Node already exists: %s/%s", parent->get_path(), name));  			RES res = ResourceLoader::load(scene_path); @@ -306,7 +308,7 @@ void MultiplayerReplicator::process_spawn_despawn(int p_from, const uint8_t *p_p  	const SceneConfig &cfg = replications[id];  	if (cfg.on_spawn_despawn_receive.is_valid()) {  		int ofs = SPAWN_CMD_OFFSET; -		bool is_raw = ((p_packet[0] & 64) >> MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1; +		bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1;  		Variant data;  		int left = p_packet_len - ofs;  		if (is_raw && left) { @@ -348,9 +350,9 @@ void MultiplayerReplicator::process_sync(int p_from, const uint8_t *p_packet, in  			}  		}  		PackedByteArray pba; -		pba.resize(p_packet_len - SPAWN_CMD_OFFSET); +		pba.resize(p_packet_len - SYNC_CMD_OFFSET);  		if (pba.size()) { -			memcpy(pba.ptrw(), p_packet, p_packet_len - SPAWN_CMD_OFFSET); +			memcpy(pba.ptrw(), p_packet + SYNC_CMD_OFFSET, p_packet_len - SYNC_CMD_OFFSET);  		}  		Variant args[4] = { p_from, id, objs, pba };  		Variant *argp[4] = { args, &args[1], &args[2], &args[3] }; @@ -464,7 +466,7 @@ Error MultiplayerReplicator::spawn_config(const ResourceUID::ID &p_id, Replicati  		SceneConfig cfg;  		cfg.mode = p_mode;  		for (int i = 0; i < p_props.size(); i++) { -			cfg.properties.push_back(StringName(p_props[i])); +			cfg.properties.push_back(p_props[i]);  		}  		cfg.on_spawn_despawn_send = p_on_send;  		cfg.on_spawn_despawn_receive = p_on_recv; @@ -503,7 +505,7 @@ Error MultiplayerReplicator::_send_spawn_despawn(int p_peer_id, const ResourceUI  	}  	MAKE_ROOM(SPAWN_CMD_OFFSET + data_size);  	uint8_t *ptr = packet_cache.ptrw(); -	ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) + ((is_raw ? 1 : 0) << MultiplayerAPI::BYTE_ONLY_OR_NO_ARGS_SHIFT); +	ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) + ((is_raw ? 1 : 0) << BYTE_OR_ZERO_SHIFT);  	encode_uint64(p_scene_id, &ptr[1]);  	if (p_data.get_type() == Variant::PACKED_BYTE_ARRAY) {  		const PackedByteArray pba = p_data; @@ -511,21 +513,21 @@ Error MultiplayerReplicator::_send_spawn_despawn(int p_peer_id, const ResourceUI  	} else if (data_size) {  		encode_variant(p_data, &ptr[SPAWN_CMD_OFFSET], data_size);  	} -	Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer(); -	network_peer->set_target_peer(p_peer_id); -	network_peer->set_transfer_channel(0); -	network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); -	return network_peer->put_packet(ptr, SPAWN_CMD_OFFSET + data_size); +	Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); +	peer->set_target_peer(p_peer_id); +	peer->set_transfer_channel(0); +	peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); +	return peer->put_packet(ptr, SPAWN_CMD_OFFSET + data_size);  }  Error MultiplayerReplicator::send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) { -	ERR_FAIL_COND_V(!multiplayer->has_network_peer(), ERR_UNCONFIGURED); +	ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);  	ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));  	const SceneConfig &cfg = replications[p_scene_id];  	if (cfg.on_spawn_despawn_send.is_valid()) {  		return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, true);  	} else { -		ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_network_server(), ERR_UNAVAILABLE, "Manual despawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests."); +		ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual despawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests.");  		NodePath path = p_path;  		Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr;  		if (path.is_empty() && obj) { @@ -540,13 +542,13 @@ Error MultiplayerReplicator::send_despawn(int p_peer_id, const ResourceUID::ID &  }  Error MultiplayerReplicator::send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) { -	ERR_FAIL_COND_V(!multiplayer->has_network_peer(), ERR_UNCONFIGURED); +	ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);  	ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));  	const SceneConfig &cfg = replications[p_scene_id];  	if (cfg.on_spawn_despawn_send.is_valid()) {  		return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, false);  	} else { -		ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_network_server(), ERR_UNAVAILABLE, "Manual spawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests."); +		ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual spawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests.");  		NodePath path = p_path;  		Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr;  		ERR_FAIL_COND_V_MSG(!obj, ERR_INVALID_PARAMETER, "Spawn default implementation requires the data to be an object."); @@ -570,7 +572,7 @@ Error MultiplayerReplicator::_spawn_despawn(ResourceUID::ID p_scene_id, Object *  		args[0] = p_peer;  		args[1] = p_scene_id;  		args[2] = p_obj; -		args[3] = true; +		args[3] = p_spawn;  		const Variant *argp[] = { &args[0], &args[1], &args[2], &args[3] };  		Callable::CallError ce;  		Variant ret; @@ -617,7 +619,7 @@ Error MultiplayerReplicator::decode_state(const ResourceUID::ID &p_scene_id, Obj  }  void MultiplayerReplicator::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) { -	if (!multiplayer->has_network_peer()) { +	if (!multiplayer->has_multiplayer_peer()) {  		return;  	}  	Node *root_node = multiplayer->get_root_node(); @@ -632,14 +634,14 @@ void MultiplayerReplicator::scene_enter_exit_notify(const String &p_scene, Node  	}  	const SceneConfig &cfg = replications[id];  	if (p_enter) { -		if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_network_server()) { +		if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server()) {  			replicated_nodes[p_node->get_instance_id()] = id;  			_track(id, p_node);  			spawn(id, p_node, 0);  		}  		emit_signal(SNAME("replicated_instance_added"), id, p_node);  	} else { -		if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_network_server() && replicated_nodes.has(p_node->get_instance_id())) { +		if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server() && replicated_nodes.has(p_node->get_instance_id())) {  			replicated_nodes.erase(p_node->get_instance_id());  			_untrack(id, p_node);  			despawn(id, p_node, 0); @@ -664,7 +666,7 @@ void MultiplayerReplicator::poll() {  		if (!E.value.sync_interval) {  			continue;  		} -		if (E.value.mode == REPLICATION_MODE_SERVER && !multiplayer->is_network_server()) { +		if (E.value.mode == REPLICATION_MODE_SERVER && !multiplayer->is_server()) {  			continue;  		}  		uint64_t time = OS::get_singleton()->get_ticks_usec(); @@ -738,8 +740,8 @@ Error MultiplayerReplicator::sync_all(const ResourceUID::ID &p_scene_id, int p_p  	return OK;  } -Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, MultiplayerPeer::TransferMode p_transfer_mode, int p_channel) { -	ERR_FAIL_COND_V(!multiplayer->has_network_peer(), ERR_UNCONFIGURED); +Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_transfer_mode, int p_channel) { +	ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);  	ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);  	const SceneConfig &cfg = replications[p_scene_id];  	ERR_FAIL_COND_V_MSG(!cfg.on_sync_send.is_valid(), ERR_UNCONFIGURED, "Sending raw sync messages is only available with custom functions"); @@ -747,11 +749,14 @@ Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_s  	uint8_t *ptr = packet_cache.ptrw();  	ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC;  	encode_uint64(p_scene_id, &ptr[1]); -	Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer(); -	network_peer->set_target_peer(p_peer_id); -	network_peer->set_transfer_channel(p_channel); -	network_peer->set_transfer_mode(p_transfer_mode); -	return network_peer->put_packet(ptr, SYNC_CMD_OFFSET + p_data.size()); +	if (p_data.size()) { +		memcpy(&ptr[SYNC_CMD_OFFSET], p_data.ptr(), p_data.size()); +	} +	Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); +	peer->set_target_peer(p_peer_id); +	peer->set_transfer_channel(p_channel); +	peer->set_transfer_mode(p_transfer_mode); +	return peer->put_packet(ptr, SYNC_CMD_OFFSET + p_data.size());  }  void MultiplayerReplicator::clear() { @@ -766,7 +771,7 @@ void MultiplayerReplicator::_bind_methods() {  	ClassDB::bind_method(D_METHOD("spawn", "scene_id", "object", "peer_id"), &MultiplayerReplicator::spawn, DEFVAL(0));  	ClassDB::bind_method(D_METHOD("send_despawn", "peer_id", "scene_id", "data", "path"), &MultiplayerReplicator::send_despawn, DEFVAL(Variant()), DEFVAL(NodePath()));  	ClassDB::bind_method(D_METHOD("send_spawn", "peer_id", "scene_id", "data", "path"), &MultiplayerReplicator::send_spawn, DEFVAL(Variant()), DEFVAL(NodePath())); -	ClassDB::bind_method(D_METHOD("send_sync", "peer_id", "scene_id", "data", "transfer_mode", "channel"), &MultiplayerReplicator::send_sync, DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); +	ClassDB::bind_method(D_METHOD("send_sync", "peer_id", "scene_id", "data", "transfer_mode", "channel"), &MultiplayerReplicator::send_sync, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));  	ClassDB::bind_method(D_METHOD("sync_all", "scene_id", "peer_id"), &MultiplayerReplicator::sync_all, DEFVAL(0));  	ClassDB::bind_method(D_METHOD("track", "scene_id", "object"), &MultiplayerReplicator::track);  	ClassDB::bind_method(D_METHOD("untrack", "scene_id", "object"), &MultiplayerReplicator::untrack); diff --git a/core/io/multiplayer_replicator.h b/core/multiplayer/multiplayer_replicator.h index 2630ad7a8a..7fa774fdf4 100644 --- a/core/io/multiplayer_replicator.h +++ b/core/multiplayer/multiplayer_replicator.h @@ -31,8 +31,9 @@  #ifndef MULTIPLAYER_REPLICATOR_H  #define MULTIPLAYER_REPLICATOR_H -#include "core/io/multiplayer_api.h" +#include "core/multiplayer/multiplayer_api.h" +#include "core/io/resource_uid.h"  #include "core/templates/hash_map.h"  #include "core/variant/typed_array.h" @@ -68,6 +69,14 @@ protected:  	static void _bind_methods();  private: +	enum { +		BYTE_OR_ZERO_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT, +	}; + +	enum { +		BYTE_OR_ZERO_FLAG = 1 << BYTE_OR_ZERO_SHIFT, +	}; +  	MultiplayerAPI *multiplayer = nullptr;  	Vector<uint8_t> packet_cache;  	Map<ResourceUID::ID, SceneConfig> replications; @@ -108,7 +117,7 @@ public:  	// Sync  	Error sync_config(const ResourceUID::ID &p_id, uint64_t p_interval, const TypedArray<StringName> &p_props = TypedArray<StringName>(), const Callable &p_on_send = Callable(), const Callable &p_on_recv = Callable());  	Error sync_all(const ResourceUID::ID &p_scene_id, int p_peer); -	Error send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, MultiplayerPeer::TransferMode p_mode, int p_channel); +	Error send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_mode, int p_channel);  	void track(const ResourceUID::ID &p_scene_id, Object *p_object);  	void untrack(const ResourceUID::ID &p_scene_id, Object *p_object); diff --git a/core/multiplayer/rpc_manager.cpp b/core/multiplayer/rpc_manager.cpp new file mode 100644 index 0000000000..d8e875c3e6 --- /dev/null +++ b/core/multiplayer/rpc_manager.cpp @@ -0,0 +1,525 @@ +/*************************************************************************/ +/*  rpc_manager.cpp                                                      */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2021 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 "core/multiplayer/rpc_manager.h" + +#include "core/debugger/engine_debugger.h" +#include "core/io/marshalls.h" +#include "core/multiplayer/multiplayer_api.h" +#include "scene/main/node.h" + +#ifdef DEBUG_ENABLED +_FORCE_INLINE_ void RPCManager::_profile_node_data(const String &p_what, ObjectID p_id) { +	if (EngineDebugger::is_profiling("multiplayer")) { +		Array values; +		values.push_back("node"); +		values.push_back(p_id); +		values.push_back(p_what); +		EngineDebugger::profiler_add_frame_data("multiplayer", values); +	} +} +#else +_FORCE_INLINE_ void RPCManager::_profile_node_data(const String &p_what, ObjectID p_id) {} +#endif + +// Returns the packet size stripping the node path added when the node is not yet cached. +int get_packet_len(uint32_t p_node_target, int p_packet_len) { +	if (p_node_target & 0x80000000) { +		int ofs = p_node_target & 0x7FFFFFFF; +		return p_packet_len - (p_packet_len - ofs); +	} else { +		return p_packet_len; +	} +} + +const Multiplayer::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) { +	const Vector<Multiplayer::RPCConfig> node_config = p_node->get_node_rpc_methods(); +	for (int i = 0; i < node_config.size(); i++) { +		if (node_config[i].name == p_method) { +			r_id = ((uint16_t)i) | (1 << 15); +			return node_config[i]; +		} +	} +	if (p_node->get_script_instance()) { +		const Vector<Multiplayer::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods(); +		for (int i = 0; i < script_config.size(); i++) { +			if (script_config[i].name == p_method) { +				r_id = (uint16_t)i; +				return script_config[i]; +			} +		} +	} +	return Multiplayer::RPCConfig(); +} + +const Multiplayer::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) { +	Vector<Multiplayer::RPCConfig> config; +	uint16_t id = p_id; +	if (id & (1 << 15)) { +		id = id & ~(1 << 15); +		config = p_node->get_node_rpc_methods(); +	} else if (p_node->get_script_instance()) { +		config = p_node->get_script_instance()->get_rpc_methods(); +	} +	if (id < config.size()) { +		return config[id]; +	} +	return Multiplayer::RPCConfig(); +} + +_FORCE_INLINE_ bool _can_call_mode(Node *p_node, Multiplayer::RPCMode mode, int p_remote_id) { +	switch (mode) { +		case Multiplayer::RPC_MODE_DISABLED: { +			return false; +		} break; +		case Multiplayer::RPC_MODE_ANY_PEER: { +			return true; +		} break; +		case Multiplayer::RPC_MODE_AUTHORITY: { +			return !p_node->is_multiplayer_authority() && p_remote_id == p_node->get_multiplayer_authority(); +		} break; +	} + +	return false; +} + +String RPCManager::get_rpc_md5(const Node *p_node) { +	String rpc_list; +	const Vector<Multiplayer::RPCConfig> node_config = p_node->get_node_rpc_methods(); +	for (int i = 0; i < node_config.size(); i++) { +		rpc_list += String(node_config[i].name); +	} +	if (p_node->get_script_instance()) { +		const Vector<Multiplayer::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods(); +		for (int i = 0; i < script_config.size(); i++) { +			rpc_list += String(script_config[i].name); +		} +	} +	return rpc_list.md5_text(); +} + +Node *RPCManager::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) { +	Node *node = nullptr; + +	if (p_node_target & 0x80000000) { +		// Use full path (not cached yet). +		int ofs = p_node_target & 0x7FFFFFFF; + +		ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared."); + +		String paths; +		paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs); + +		NodePath np = paths; + +		node = multiplayer->get_root_node()->get_node(np); + +		if (!node) { +			ERR_PRINT("Failed to get path from RPC: " + String(np) + "."); +		} +		return node; +	} else { +		// Use cached path. +		return multiplayer->get_cached_node(p_from, p_node_target); +	} +} + +void RPCManager::process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) { +	// Extract packet meta +	int packet_min_size = 1; +	int name_id_offset = 1; +	ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); +	// Compute the meta size, which depends on the compression level. +	int node_id_compression = (p_packet[0] & NODE_ID_COMPRESSION_FLAG) >> NODE_ID_COMPRESSION_SHIFT; +	int name_id_compression = (p_packet[0] & NAME_ID_COMPRESSION_FLAG) >> NAME_ID_COMPRESSION_SHIFT; + +	switch (node_id_compression) { +		case NETWORK_NODE_ID_COMPRESSION_8: +			packet_min_size += 1; +			name_id_offset += 1; +			break; +		case NETWORK_NODE_ID_COMPRESSION_16: +			packet_min_size += 2; +			name_id_offset += 2; +			break; +		case NETWORK_NODE_ID_COMPRESSION_32: +			packet_min_size += 4; +			name_id_offset += 4; +			break; +		default: +			ERR_FAIL_MSG("Was not possible to extract the node id compression mode."); +	} +	switch (name_id_compression) { +		case NETWORK_NAME_ID_COMPRESSION_8: +			packet_min_size += 1; +			break; +		case NETWORK_NAME_ID_COMPRESSION_16: +			packet_min_size += 2; +			break; +		default: +			ERR_FAIL_MSG("Was not possible to extract the name id compression mode."); +	} +	ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); + +	uint32_t node_target = 0; +	switch (node_id_compression) { +		case NETWORK_NODE_ID_COMPRESSION_8: +			node_target = p_packet[1]; +			break; +		case NETWORK_NODE_ID_COMPRESSION_16: +			node_target = decode_uint16(p_packet + 1); +			break; +		case NETWORK_NODE_ID_COMPRESSION_32: +			node_target = decode_uint32(p_packet + 1); +			break; +		default: +			// Unreachable, checked before. +			CRASH_NOW(); +	} + +	Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len); +	ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found."); + +	uint16_t name_id = 0; +	switch (name_id_compression) { +		case NETWORK_NAME_ID_COMPRESSION_8: +			name_id = p_packet[name_id_offset]; +			break; +		case NETWORK_NAME_ID_COMPRESSION_16: +			name_id = decode_uint16(p_packet + name_id_offset); +			break; +		default: +			// Unreachable, checked before. +			CRASH_NOW(); +	} + +	const int packet_len = get_packet_len(node_target, p_packet_len); +	_process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size); +} + +void RPCManager::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { +	ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small."); + +	// Check that remote can call the RPC on this node. +	const Multiplayer::RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id); +	ERR_FAIL_COND(config.name == StringName()); + +	bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from); +	ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_multiplayer_authority()) + "."); + +	int argc = 0; +	bool byte_only = false; + +	const bool byte_only_or_no_args = p_packet[0] & BYTE_ONLY_OR_NO_ARGS_FLAG; +	if (byte_only_or_no_args) { +		if (p_offset < p_packet_len) { +			// This packet contains only bytes. +			argc = 1; +			byte_only = true; +		} else { +			// This rpc calls a method without parameters. +		} +	} else { +		// Normal variant, takes the argument count from the packet. +		ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); +		argc = p_packet[p_offset]; +		p_offset += 1; +	} + +	Vector<Variant> args; +	Vector<const Variant *> argp; +	args.resize(argc); +	argp.resize(argc); + +#ifdef DEBUG_ENABLED +	_profile_node_data("in_rpc", p_node->get_instance_id()); +#endif + +	if (byte_only) { +		Vector<uint8_t> pure_data; +		const int len = p_packet_len - p_offset; +		pure_data.resize(len); +		memcpy(pure_data.ptrw(), &p_packet[p_offset], len); +		args.write[0] = pure_data; +		argp.write[0] = &args[0]; +		p_offset += len; +	} else { +		for (int i = 0; i < argc; i++) { +			ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); + +			int vlen; +			Error err = multiplayer->decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); +			ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument."); + +			argp.write[i] = &args[i]; +			p_offset += vlen; +		} +	} + +	Callable::CallError ce; + +	p_node->call(config.name, (const Variant **)argp.ptr(), argc, ce); +	if (ce.error != Callable::CallError::CALL_OK) { +		String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce); +		error = "RPC - " + error; +		ERR_PRINT(error); +	} +} + +void RPCManager::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) { +	Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); +	ERR_FAIL_COND_MSG(peer.is_null(), "Attempt to call RPC without active multiplayer peer."); + +	ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to call RPC while multiplayer peer is not connected yet."); + +	ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to call RPC while multiplayer peer is disconnected."); + +	ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments (>255)."); + +	if (p_to != 0 && !multiplayer->get_connected_peers().has(ABS(p_to))) { +		ERR_FAIL_COND_MSG(p_to == peer->get_unique_id(), "Attempt to call RPC on yourself! Peer unique ID: " + itos(peer->get_unique_id()) + "."); + +		ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + "."); +	} + +	NodePath from_path = (multiplayer->get_root_node()->get_path()).rel_path_to(p_from->get_path()); +	ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!"); + +	// See if all peers have cached path (if so, call can be fast). +	int psc_id; +	const bool has_all_peers = multiplayer->send_confirm_path(p_from, from_path, p_to, psc_id); + +	// Create base packet, lots of hardcode because it must be tight. + +	int ofs = 0; + +#define MAKE_ROOM(m_amount)             \ +	if (packet_cache.size() < m_amount) \ +		packet_cache.resize(m_amount); + +	// Encode meta. +	uint8_t command_type = MultiplayerAPI::NETWORK_COMMAND_REMOTE_CALL; +	uint8_t node_id_compression = UINT8_MAX; +	uint8_t name_id_compression = UINT8_MAX; +	bool byte_only_or_no_args = false; + +	MAKE_ROOM(1); +	// The meta is composed along the way, so just set 0 for now. +	packet_cache.write[0] = 0; +	ofs += 1; + +	// Encode Node ID. +	if (has_all_peers) { +		// Compress the node ID only if all the target peers already know it. +		if (psc_id >= 0 && psc_id <= 255) { +			// We can encode the id in 1 byte +			node_id_compression = NETWORK_NODE_ID_COMPRESSION_8; +			MAKE_ROOM(ofs + 1); +			packet_cache.write[ofs] = static_cast<uint8_t>(psc_id); +			ofs += 1; +		} else if (psc_id >= 0 && psc_id <= 65535) { +			// We can encode the id in 2 bytes +			node_id_compression = NETWORK_NODE_ID_COMPRESSION_16; +			MAKE_ROOM(ofs + 2); +			encode_uint16(static_cast<uint16_t>(psc_id), &(packet_cache.write[ofs])); +			ofs += 2; +		} else { +			// Too big, let's use 4 bytes. +			node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; +			MAKE_ROOM(ofs + 4); +			encode_uint32(psc_id, &(packet_cache.write[ofs])); +			ofs += 4; +		} +	} else { +		// The targets don't know the node yet, so we need to use 32 bits int. +		node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; +		MAKE_ROOM(ofs + 4); +		encode_uint32(psc_id, &(packet_cache.write[ofs])); +		ofs += 4; +	} + +	// Encode method ID +	if (p_rpc_id <= UINT8_MAX) { +		// The ID fits in 1 byte +		name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; +		MAKE_ROOM(ofs + 1); +		packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id); +		ofs += 1; +	} else { +		// The ID is larger, let's use 2 bytes +		name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; +		MAKE_ROOM(ofs + 2); +		encode_uint16(p_rpc_id, &(packet_cache.write[ofs])); +		ofs += 2; +	} + +	if (p_argcount == 0) { +		byte_only_or_no_args = true; +	} else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) { +		byte_only_or_no_args = true; +		// Special optimization when only the byte vector is sent. +		const Vector<uint8_t> data = *p_arg[0]; +		MAKE_ROOM(ofs + data.size()); +		memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size()); +		ofs += data.size(); +	} else { +		// Arguments +		MAKE_ROOM(ofs + 1); +		packet_cache.write[ofs] = p_argcount; +		ofs += 1; +		for (int i = 0; i < p_argcount; i++) { +			int len(0); +			Error err = multiplayer->encode_and_compress_variant(*p_arg[i], nullptr, len); +			ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); +			MAKE_ROOM(ofs + len); +			multiplayer->encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len); +			ofs += len; +		} +	} + +	ERR_FAIL_COND(command_type > 7); +	ERR_FAIL_COND(node_id_compression > 3); +	ERR_FAIL_COND(name_id_compression > 1); + +	// We can now set the meta +	packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + (byte_only_or_no_args ? BYTE_ONLY_OR_NO_ARGS_FLAG : 0); + +#ifdef DEBUG_ENABLED +	multiplayer->profile_bandwidth("out", ofs); +#endif + +	// Take chance and set transfer mode, since all send methods will use it. +	peer->set_transfer_channel(p_config.channel); +	peer->set_transfer_mode(p_config.transfer_mode); + +	if (has_all_peers) { +		// They all have verified paths, so send fast. +		peer->set_target_peer(p_to); // To all of you. +		peer->put_packet(packet_cache.ptr(), ofs); // A message with love. +	} else { +		// Unreachable because the node ID is never compressed if the peers doesn't know it. +		CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32); + +		// Not all verified path, so send one by one. + +		// Append path at the end, since we will need it for some packets. +		CharString pname = String(from_path).utf8(); +		int path_len = encode_cstring(pname.get_data(), nullptr); +		MAKE_ROOM(ofs + path_len); +		encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); + +		for (const int &P : multiplayer->get_connected_peers()) { +			if (p_to < 0 && P == -p_to) { +				continue; // Continue, excluded. +			} + +			if (p_to > 0 && P != p_to) { +				continue; // Continue, not for this peer. +			} + +			bool confirmed = multiplayer->is_cache_confirmed(from_path, P); + +			peer->set_target_peer(P); // To this one specifically. + +			if (confirmed) { +				// This one confirmed path, so use id. +				encode_uint32(psc_id, &(packet_cache.write[1])); +				peer->put_packet(packet_cache.ptr(), ofs); +			} else { +				// This one did not confirm path yet, so use entire path (sorry!). +				encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag. +				peer->put_packet(packet_cache.ptr(), ofs + path_len); +			} +		} +	} +} + +void RPCManager::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { +	Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); +	ERR_FAIL_COND_MSG(!peer.is_valid(), "Trying to call an RPC while no multiplayer peer is active."); +	ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); +	ERR_FAIL_COND_MSG(peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a multiplayer peer which is not connected."); + +	int node_id = peer->get_unique_id(); +	bool call_local_native = false; +	bool call_local_script = false; +	uint16_t rpc_id = UINT16_MAX; +	const Multiplayer::RPCConfig config = _get_rpc_config(p_node, p_method, rpc_id); +	ERR_FAIL_COND_MSG(config.name == StringName(), +			vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, p_node->get_path())); +	if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { +		if (rpc_id & (1 << 15)) { +			call_local_native = config.call_local; +		} else { +			call_local_script = config.call_local; +		} +	} + +	if (p_peer_id != node_id) { +#ifdef DEBUG_ENABLED +		_profile_node_data("out_rpc", p_node->get_instance_id()); +#endif + +		_send_rpc(p_node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount); +	} + +	if (call_local_native) { +		Callable::CallError ce; + +		multiplayer->set_remote_sender_override(peer->get_unique_id()); +		p_node->call(p_method, p_arg, p_argcount, ce); +		multiplayer->set_remote_sender_override(0); + +		if (ce.error != Callable::CallError::CALL_OK) { +			String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); +			error = "rpc() aborted in local call:  - " + error + "."; +			ERR_PRINT(error); +			return; +		} +	} + +	if (call_local_script) { +		Callable::CallError ce; +		ce.error = Callable::CallError::CALL_OK; + +		multiplayer->set_remote_sender_override(peer->get_unique_id()); +		p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce); +		multiplayer->set_remote_sender_override(0); + +		if (ce.error != Callable::CallError::CALL_OK) { +			String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); +			error = "rpc() aborted in script local call:  - " + error + "."; +			ERR_PRINT(error); +			return; +		} +	} + +	ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.call_local, "RPC '" + p_method + "' on yourself is not allowed by selected mode."); +} diff --git a/core/multiplayer/rpc_manager.h b/core/multiplayer/rpc_manager.h new file mode 100644 index 0000000000..7b99dee226 --- /dev/null +++ b/core/multiplayer/rpc_manager.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/*  rpc_manager.h                                                        */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2021 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 MULTIPLAYER_RPC_H +#define MULTIPLAYER_RPC_H + +#include "core/multiplayer/multiplayer.h" +#include "core/multiplayer/multiplayer_api.h" +#include "core/object/ref_counted.h" + +class RPCManager : public RefCounted { +	GDCLASS(RPCManager, RefCounted); + +private: +	enum NetworkNodeIdCompression { +		NETWORK_NODE_ID_COMPRESSION_8 = 0, +		NETWORK_NODE_ID_COMPRESSION_16, +		NETWORK_NODE_ID_COMPRESSION_32, +	}; + +	enum NetworkNameIdCompression { +		NETWORK_NAME_ID_COMPRESSION_8 = 0, +		NETWORK_NAME_ID_COMPRESSION_16, +	}; + +	// The RPC meta is composed by a single byte that contains (starting from the least significant bit): +	// - `NetworkCommands` in the first four bits. +	// - `NetworkNodeIdCompression` in the next 2 bits. +	// - `NetworkNameIdCompression` in the next 1 bit. +	// - `byte_only_or_no_args` in the next 1 bit. +	enum { +		NODE_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT, // 2 bits for this. +		NAME_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_2_SHIFT, +		BYTE_ONLY_OR_NO_ARGS_SHIFT = MultiplayerAPI::CMD_FLAG_3_SHIFT, +	}; + +	enum { +		NODE_ID_COMPRESSION_FLAG = (1 << NODE_ID_COMPRESSION_SHIFT) | (1 << (NODE_ID_COMPRESSION_SHIFT + 1)), // 2 bits for this. +		NAME_ID_COMPRESSION_FLAG = (1 << NAME_ID_COMPRESSION_SHIFT), +		BYTE_ONLY_OR_NO_ARGS_FLAG = (1 << BYTE_ONLY_OR_NO_ARGS_SHIFT), +	}; + +	MultiplayerAPI *multiplayer = nullptr; +	Vector<uint8_t> packet_cache; + +protected: +	_FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id); +	void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); + +	void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount); +	Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len); + +public: +	// Called by Node.rpc +	void rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount); +	void process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len); + +	String get_rpc_md5(const Node *p_node); +	RPCManager(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; } +}; + +#endif // MULTIPLAYER_RPC_H diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index e268a8d292..bbd3b7b8de 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -37,8 +37,6 @@  #define OBJTYPE_RLOCK RWLockRead _rw_lockr_(lock);  #define OBJTYPE_WLOCK RWLockWrite _rw_lockw_(lock); -#ifdef DEBUG_METHODS_ENABLED -  MethodDefinition D_METHOD(const char *p_name) {  	MethodDefinition md;  	md.name = StaticCString::create(p_name); @@ -226,8 +224,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_  	return md;  } -#endif -  ClassDB::APIType ClassDB::current_api = API_CORE;  void ClassDB::set_current_api(APIType p_api) { @@ -501,22 +497,6 @@ void ClassDB::add_compatibility_class(const StringName &p_class, const StringNam  	compat_classes[p_class] = p_fallback;  } -thread_local bool initializing_with_extension = false; -thread_local ObjectNativeExtension *initializing_extension = nullptr; -thread_local GDExtensionClassInstancePtr initializing_extension_instance = nullptr; - -void ClassDB::instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance, Object *p_base) { -	if (initializing_with_extension) { -		*r_extension = initializing_extension; -		*r_extension_instance = initializing_extension_instance; -		initializing_with_extension = false; -		initializing_extension->set_object_instance(*r_extension_instance, p_base); -	} else { -		*r_extension = nullptr; -		*r_extension_instance = nullptr; -	} -} -  Object *ClassDB::instantiate(const StringName &p_class) {  	ClassInfo *ti;  	{ @@ -529,7 +509,7 @@ Object *ClassDB::instantiate(const StringName &p_class) {  		}  		ERR_FAIL_COND_V_MSG(!ti, nullptr, "Cannot get class '" + String(p_class) + "'.");  		ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled."); -		ERR_FAIL_COND_V(!ti->creation_func, nullptr); +		ERR_FAIL_COND_V_MSG(!ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated.");  	}  #ifdef TOOLS_ENABLED  	if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { @@ -537,12 +517,31 @@ Object *ClassDB::instantiate(const StringName &p_class) {  		return nullptr;  	}  #endif -	if (ti->native_extension) { -		initializing_with_extension = true; -		initializing_extension = ti->native_extension; -		initializing_extension_instance = ti->native_extension->create_instance(ti->native_extension->class_userdata); +	if (ti->native_extension && ti->native_extension->create_instance) { +		return (Object *)ti->native_extension->create_instance(ti->native_extension->class_userdata); +	} else { +		return ti->creation_func(); +	} +} + +void ClassDB::set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance) { +	ERR_FAIL_COND(!p_object); +	ClassInfo *ti; +	{ +		OBJTYPE_RLOCK; +		ti = classes.getptr(p_class); +		if (!ti || ti->disabled || !ti->creation_func || (ti->native_extension && !ti->native_extension->create_instance)) { +			if (compat_classes.has(p_class)) { +				ti = classes.getptr(compat_classes[p_class]); +			} +		} +		ERR_FAIL_COND_MSG(!ti, "Cannot get class '" + String(p_class) + "'."); +		ERR_FAIL_COND_MSG(ti->disabled, "Class '" + String(p_class) + "' is disabled."); +		ERR_FAIL_COND_MSG(!ti->native_extension, "Class '" + String(p_class) + "' has no native extension.");  	} -	return ti->creation_func(); + +	p_object->_extension = ti->native_extension; +	p_object->_extension_instance = p_instance;  }  bool ClassDB::can_instantiate(const StringName &p_class) { @@ -580,7 +579,6 @@ 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(); @@ -601,7 +599,6 @@ static MethodInfo info_from_bind(MethodBind *p_method) {  	return minfo;  } -#endif  void ClassDB::get_method_list(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) {  	OBJTYPE_RLOCK; @@ -641,9 +638,8 @@ void ClassDB::get_method_list(const StringName &p_class, List<MethodInfo> *p_met  		while ((K = type->method_map.next(K))) {  			MethodBind *m = type->method_map[*K]; -			MethodInfo mi; -			mi.name = m->get_name(); -			p_methods->push_back(mi); +			MethodInfo minfo = info_from_bind(m); +			p_methods->push_back(minfo);  		}  #endif @@ -689,9 +685,8 @@ bool ClassDB::get_method_info(const StringName &p_class, const StringName &p_met  		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; +				MethodInfo minfo = info_from_bind(m); +				*r_info = minfo;  			}  			return true;  		} @@ -736,7 +731,7 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName  	type->constant_map[p_name] = p_constant;  	String enum_name = p_enum; -	if (enum_name != String()) { +	if (!enum_name.is_empty()) {  		if (enum_name.find(".") != -1) {  			enum_name = enum_name.get_slicec('.', 1);  		} @@ -1028,6 +1023,18 @@ void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_n  	type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_SUBGROUP));  } +void ClassDB::add_property_array_count(const StringName &p_class, const String &p_label, const StringName &p_count_property, const StringName &p_count_setter, const StringName &p_count_getter, const String &p_array_element_prefix, uint32_t p_count_usage) { +	add_property(p_class, PropertyInfo(Variant::INT, p_count_property, PROPERTY_HINT_NONE, "", p_count_usage | PROPERTY_USAGE_ARRAY, vformat("%s,%s", p_label, p_array_element_prefix)), p_count_setter, p_count_getter); +} + +void ClassDB::add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix) { +	OBJTYPE_WLOCK; +	ClassInfo *type = classes.getptr(p_class); +	ERR_FAIL_COND(!type); + +	type->property_list.push_back(PropertyInfo(Variant::NIL, p_path, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, p_array_element_prefix)); +} +  // NOTE: For implementation simplicity reasons, this method doesn't allow setters to have optional arguments at the end.  void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) {  	lock.read_lock(); @@ -1390,13 +1397,8 @@ void ClassDB::bind_method_custom(const StringName &p_class, MethodBind *p_method  	type->method_map[p_method->get_name()] = p_method;  } -#ifdef DEBUG_METHODS_ENABLED  MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount) {  	StringName mdname = method_name.name; -#else -MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const char *method_name, const Variant **p_defs, int p_defcount) { -	StringName mdname = StaticCString::create(method_name); -#endif  	OBJTYPE_WLOCK;  	ERR_FAIL_COND_V(!p_bind, nullptr); diff --git a/core/object/class_db.h b/core/object/class_db.h index 166aa35469..ca9c1def29 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -45,8 +45,6 @@  #define DEFVAL(m_defval) (m_defval) -#ifdef DEBUG_METHODS_ENABLED -  struct MethodDefinition {  	StringName name;  	Vector<StringName> args; @@ -72,26 +70,6 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_  MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12);  MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12, const char *p_arg13); -#else - -//#define NO_VARIADIC_MACROS - -#ifdef NO_VARIADIC_MACROS - -static _FORCE_INLINE_ const char *D_METHOD(const char *m_name, ...) { -	return m_name; -} - -#else - -// When DEBUG_METHODS_ENABLED is set this will let the engine know -// the argument names for easier debugging. -#define D_METHOD(m_c, ...) m_c - -#endif - -#endif -  class ClassDB {  public:  	enum APIType { @@ -156,11 +134,7 @@ public:  	static HashMap<StringName, StringName> resource_base_extensions;  	static HashMap<StringName, StringName> compat_classes; -#ifdef DEBUG_METHODS_ENABLED  	static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount); -#else -	static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const char *method_name, const Variant **p_defs, int p_defcount); -#endif  	static APIType current_api; @@ -190,6 +164,7 @@ public:  		t->creation_func = &creator<T>;  		t->exposed = true;  		t->class_ptr = T::get_class_ptr_static(); +		t->api = current_api;  		T::register_custom_data_to_otdb();  	} @@ -201,6 +176,7 @@ public:  		ERR_FAIL_COND(!t);  		t->exposed = true;  		t->class_ptr = T::get_class_ptr_static(); +		t->api = current_api;  		//nothing  	} @@ -221,6 +197,7 @@ public:  		t->creation_func = &_create_ptr_func<T>;  		t->exposed = true;  		t->class_ptr = T::get_class_ptr_static(); +		t->api = current_api;  		T::register_custom_data_to_otdb();  	} @@ -234,7 +211,7 @@ public:  	static bool is_parent_class(const StringName &p_class, const StringName &p_inherits);  	static bool can_instantiate(const StringName &p_class);  	static Object *instantiate(const StringName &p_class); -	static void instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance, Object *p_base); +	static void set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance);  	static APIType get_api_type(const StringName &p_class); @@ -353,6 +330,8 @@ public:  	static void add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix = "");  	static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix = ""); +	static void add_property_array_count(const StringName &p_class, const String &p_label, const StringName &p_count_property, const StringName &p_count_setter, const StringName &p_count_getter, const String &p_array_element_prefix, uint32_t p_count_usage = PROPERTY_USAGE_DEFAULT); +	static void add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix);  	static void add_property(const 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(const StringName &p_class, const StringName &p_name, const Variant &p_default);  	static void add_linked_property(const StringName &p_class, const String &p_property, const String &p_linked_property); diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index 86c2891e5d..e961745d96 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -1,7 +1,8 @@  proto = """  #define GDVIRTUAL$VER($RET m_name $ARG) \\  StringName _gdvirtual_##m_name##_sn = #m_name;\\ -GDNativeExtensionClassCallVirtual _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, #m_name) : (GDNativeExtensionClassCallVirtual) nullptr;\\ +mutable bool _gdvirtual_##m_name##_initialized = false;\\ +mutable GDNativeExtensionClassCallVirtual _gdvirtual_##m_name = nullptr;\\  _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\  	ScriptInstance *script_instance = ((Object*)(this))->get_script_instance();\\  	if (script_instance) {\\ @@ -13,6 +14,10 @@ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\  			return true;\\  		}    \\  	}\\ +    if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ +        _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, #m_name) : (GDNativeExtensionClassCallVirtual) nullptr;\\ +        _gdvirtual_##m_name##_initialized = true;\\ +    }\\  	if (_gdvirtual_##m_name) {\\  		$CALLPTRARGS\\  		$CALLPTRRETDEF\\ @@ -28,6 +33,10 @@ _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\  	if (script_instance) {\\  	    return script_instance->has_method(_gdvirtual_##m_name##_sn);\\  	}\\ +    if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ +        _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, #m_name) : (GDNativeExtensionClassCallVirtual) nullptr;\\ +        _gdvirtual_##m_name##_initialized = true;\\ +    }\\  	if (_gdvirtual_##m_name) {\\  	    return true;\\  	}\\ diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp index 4751c69f1e..736e940846 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -227,16 +227,16 @@ void MessageQueue::statistics() {  	print_line("TOTAL BYTES: " + itos(buffer_end));  	print_line("NULL count: " + itos(null_count)); -	for (Map<StringName, int>::Element *E = set_count.front(); E; E = E->next()) { -		print_line("SET " + E->key() + ": " + itos(E->get())); +	for (const KeyValue<StringName, int> &E : set_count) { +		print_line("SET " + E.key + ": " + itos(E.value));  	} -	for (Map<Callable, int>::Element *E = call_count.front(); E; E = E->next()) { -		print_line("CALL " + E->key() + ": " + itos(E->get())); +	for (const KeyValue<Callable, int> &E : call_count) { +		print_line("CALL " + E.key + ": " + itos(E.value));  	} -	for (Map<int, int>::Element *E = notify_count.front(); E; E = E->next()) { -		print_line("NOTIFY " + itos(E->key()) + ": " + itos(E->get())); +	for (const KeyValue<int, int> &E : notify_count) { +		print_line("NOTIFY " + itos(E.key) + ": " + itos(E.value));  	}  } diff --git a/core/object/method_bind.cpp b/core/object/method_bind.cpp index c53104fe3f..642e27c41d 100644 --- a/core/object/method_bind.cpp +++ b/core/object/method_bind.cpp @@ -63,12 +63,15 @@ uint32_t MethodBind::get_hash() const {  	return hash;  } -#ifdef DEBUG_METHODS_ENABLED  PropertyInfo MethodBind::get_argument_info(int p_argument) const {  	ERR_FAIL_INDEX_V(p_argument, get_argument_count(), PropertyInfo());  	PropertyInfo info = _gen_argument_type_info(p_argument); +#ifdef DEBUG_METHODS_ENABLED  	info.name = p_argument < arg_names.size() ? String(arg_names[p_argument]) : String("arg" + itos(p_argument)); +#else +	info.name = String("arg" + itos(p_argument)); +#endif  	return info;  } @@ -76,7 +79,6 @@ PropertyInfo MethodBind::get_return_info() const {  	return _gen_argument_type_info(-1);  } -#endif  void MethodBind::_set_const(bool p_const) {  	_const = p_const;  } @@ -109,7 +111,6 @@ void MethodBind::set_default_arguments(const Vector<Variant> &p_defargs) {  	default_argument_count = default_arguments.size();  } -#ifdef DEBUG_METHODS_ENABLED  void MethodBind::_generate_argument_types(int p_count) {  	set_argument_count(p_count); @@ -123,17 +124,13 @@ void MethodBind::_generate_argument_types(int p_count) {  	argument_types = argt;  } -#endif -  MethodBind::MethodBind() {  	static int last_id = 0;  	method_id = last_id++;  }  MethodBind::~MethodBind() { -#ifdef DEBUG_METHODS_ENABLED  	if (argument_types) {  		memdelete_arr(argument_types);  	} -#endif  } diff --git a/core/object/method_bind.h b/core/object/method_bind.h index b0b379873e..ee003099a0 100644 --- a/core/object/method_bind.h +++ b/core/object/method_bind.h @@ -64,18 +64,16 @@ class MethodBind {  	bool _returns = false;  protected: -#ifdef DEBUG_METHODS_ENABLED  	Variant::Type *argument_types = nullptr; +#ifdef DEBUG_METHODS_ENABLED  	Vector<StringName> arg_names;  #endif  	void _set_const(bool p_const);  	void _set_returns(bool p_returns); -#ifdef DEBUG_METHODS_ENABLED  	virtual Variant::Type _gen_argument_type(int p_arg) const = 0;  	virtual PropertyInfo _gen_argument_type_info(int p_arg) const = 0;  	void _generate_argument_types(int p_count); -#endif  	void set_argument_count(int p_count) { argument_count = p_count; }  public: @@ -102,7 +100,6 @@ public:  		}  	} -#ifdef DEBUG_METHODS_ENABLED  	_FORCE_INLINE_ Variant::Type get_argument_type(int p_argument) const {  		ERR_FAIL_COND_V(p_argument < -1 || p_argument > argument_count, Variant::NIL);  		return argument_types[p_argument + 1]; @@ -111,6 +108,7 @@ public:  	PropertyInfo get_argument_info(int p_argument) const;  	PropertyInfo get_return_info() const; +#ifdef DEBUG_METHODS_ENABLED  	void set_argument_names(const Vector<StringName> &p_names); // Set by ClassDB, can't be inferred otherwise.  	Vector<StringName> get_argument_names() const; @@ -149,12 +147,9 @@ public:  protected:  	NativeCall call_method = nullptr; -#ifdef DEBUG_METHODS_ENABLED  	MethodInfo arguments; -#endif  public: -#ifdef DEBUG_METHODS_ENABLED  	virtual PropertyInfo _gen_argument_type_info(int p_arg) const {  		if (p_arg < 0) {  			return arguments.return_val; @@ -169,13 +164,10 @@ public:  		return _gen_argument_type_info(p_arg).type;  	} +#ifdef DEBUG_METHODS_ENABLED  	virtual GodotTypeInfo::Metadata get_argument_meta(int) const {  		return GodotTypeInfo::METADATA_NONE;  	} -#else -	virtual Variant::Type _gen_argument_type(int p_arg) const { -		return Variant::NIL; -	}  #endif  	virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { @@ -185,25 +177,29 @@ public:  	void set_method_info(const MethodInfo &p_info, bool p_return_nil_is_variant) {  		set_argument_count(p_info.arguments.size()); -#ifdef DEBUG_METHODS_ENABLED  		Variant::Type *at = memnew_arr(Variant::Type, p_info.arguments.size() + 1);  		at[0] = p_info.return_val.type;  		if (p_info.arguments.size()) { +#ifdef DEBUG_METHODS_ENABLED  			Vector<StringName> names;  			names.resize(p_info.arguments.size()); +#endif  			for (int i = 0; i < p_info.arguments.size(); i++) {  				at[i + 1] = p_info.arguments[i].type; +#ifdef DEBUG_METHODS_ENABLED  				names.write[i] = p_info.arguments[i].name; +#endif  			} +#ifdef DEBUG_METHODS_ENABLED  			set_argument_names(names); +#endif  		}  		argument_types = at;  		arguments = p_info;  		if (p_return_nil_is_variant) {  			arguments.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;  		} -#endif  	}  	virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { @@ -248,7 +244,6 @@ class MethodBindT : public MethodBind {  	void (MB_T::*method)(P...);  protected: -#ifdef DEBUG_METHODS_ENABLED  // GCC raises warnings in the case P = {} as the comparison is always false...  #if defined(__GNUC__) && !defined(__clang__)  #pragma GCC diagnostic push @@ -270,7 +265,6 @@ protected:  		call_get_argument_type_info<P...>(p_arg, pi);  		return pi;  	} -#endif  public:  #ifdef DEBUG_METHODS_ENABLED @@ -298,9 +292,7 @@ public:  	MethodBindT(void (MB_T::*p_method)(P...)) {  		method = p_method; -#ifdef DEBUG_METHODS_ENABLED  		_generate_argument_types(sizeof...(P)); -#endif  		set_argument_count(sizeof...(P));  	}  }; @@ -327,7 +319,6 @@ class MethodBindTC : public MethodBind {  	void (MB_T::*method)(P...) const;  protected: -#ifdef DEBUG_METHODS_ENABLED  // GCC raises warnings in the case P = {} as the comparison is always false...  #if defined(__GNUC__) && !defined(__clang__)  #pragma GCC diagnostic push @@ -349,7 +340,6 @@ protected:  		call_get_argument_type_info<P...>(p_arg, pi);  		return pi;  	} -#endif  public:  #ifdef DEBUG_METHODS_ENABLED @@ -378,9 +368,7 @@ public:  	MethodBindTC(void (MB_T::*p_method)(P...) const) {  		method = p_method;  		_set_const(true); -#ifdef DEBUG_METHODS_ENABLED  		_generate_argument_types(sizeof...(P)); -#endif  		set_argument_count(sizeof...(P));  	}  }; @@ -408,7 +396,6 @@ class MethodBindTR : public MethodBind {  	(P...);  protected: -#ifdef DEBUG_METHODS_ENABLED  // GCC raises warnings in the case P = {} as the comparison is always false...  #if defined(__GNUC__) && !defined(__clang__)  #pragma GCC diagnostic push @@ -434,7 +421,6 @@ protected:  #if defined(__GNUC__) && !defined(__clang__)  #pragma GCC diagnostic pop  #endif -#endif  public:  #ifdef DEBUG_METHODS_ENABLED @@ -468,9 +454,7 @@ public:  	MethodBindTR(R (MB_T::*p_method)(P...)) {  		method = p_method;  		_set_returns(true); -#ifdef DEBUG_METHODS_ENABLED  		_generate_argument_types(sizeof...(P)); -#endif  		set_argument_count(sizeof...(P));  	}  }; @@ -499,7 +483,6 @@ class MethodBindTRC : public MethodBind {  	(P...) const;  protected: -#ifdef DEBUG_METHODS_ENABLED  // GCC raises warnings in the case P = {} as the comparison is always false...  #if defined(__GNUC__) && !defined(__clang__)  #pragma GCC diagnostic push @@ -525,7 +508,6 @@ protected:  #if defined(__GNUC__) && !defined(__clang__)  #pragma GCC diagnostic pop  #endif -#endif  public:  #ifdef DEBUG_METHODS_ENABLED @@ -560,9 +542,7 @@ public:  		method = p_method;  		_set_returns(true);  		_set_const(true); -#ifdef DEBUG_METHODS_ENABLED  		_generate_argument_types(sizeof...(P)); -#endif  		set_argument_count(sizeof...(P));  	}  }; diff --git a/core/object/object.cpp b/core/object/object.cpp index 3335942fb3..14d4e0bee6 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -628,7 +628,10 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons  		script_instance->get_property_list(p_list);  	} -	_get_property_listv(p_list, p_reversed); +	if (_extension) { +		p_list->push_back(PropertyInfo(Variant::NIL, _extension->class_name, PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); +		ClassDB::get_property_list(_extension->class_name, p_list, true, this); +	}  	if (_extension && _extension->get_property_list) {  		uint32_t pcount; @@ -641,11 +644,13 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons  		}  	} +	_get_property_listv(p_list, p_reversed); +  	if (!is_class("Script")) { // can still be set, but this is for user-friendliness  		p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT));  	}  	if (!metadata.is_empty()) { -		p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); +		p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));  	}  	if (script_instance && !p_reversed) {  		p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); @@ -985,7 +990,7 @@ void Object::get_meta_list(List<StringName> *p_list) const {  }  void Object::add_user_signal(const MethodInfo &p_signal) { -	ERR_FAIL_COND_MSG(p_signal.name == "", "Signal name cannot be empty."); +	ERR_FAIL_COND_MSG(p_signal.name.is_empty(), "Signal name cannot be empty.");  	ERR_FAIL_COND_MSG(ClassDB::has_signal(get_class_name(), p_signal.name), "User signal's name conflicts with a built-in signal of '" + get_class_name() + "'.");  	ERR_FAIL_COND_MSG(signal_map.has(p_signal.name), "Trying to add already existing signal '" + p_signal.name + "'.");  	SignalData s; @@ -1248,7 +1253,7 @@ void Object::get_signal_list(List<MethodInfo> *p_signals) const {  	const StringName *S = nullptr;  	while ((S = signal_map.next(S))) { -		if (signal_map[*S].user.name != "") { +		if (!signal_map[*S].user.name.is_empty()) {  			//user signal  			p_signals->push_back(signal_map[*S].user);  		} @@ -1398,18 +1403,18 @@ void Object::_disconnect(const StringName &p_signal, const Callable &p_callable,  	SignalData *s = signal_map.getptr(p_signal);  	if (!s) {  		bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal) || -							   (!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal)); +				(!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal));  		ERR_FAIL_COND_MSG(signal_is_valid, "Attempt to disconnect a nonexistent connection from '" + to_string() + "'. Signal: '" + p_signal + "', callable: '" + p_callable + "'.");  	}  	ERR_FAIL_COND_MSG(!s, vformat("Disconnecting nonexistent signal '%s' in %s.", p_signal, to_string()));  	ERR_FAIL_COND_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), "Disconnecting nonexistent signal '" + p_signal + "', callable: " + p_callable + "."); -	SignalData::Slot *slot = &s->slot_map[p_callable]; +	SignalData::Slot *slot = &s->slot_map[*p_callable.get_base_comparator()];  	if (!p_force) {  		slot->reference_count--; // by default is zero, if it was not referenced it will go below it -		if (slot->reference_count >= 0) { +		if (slot->reference_count > 0) {  			return;  		}  	} @@ -1475,7 +1480,7 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) {  				return;  			} -			if (!r->get_path().begins_with("res://") || r->get_path().find("::") == -1) { +			if (!r->is_built_in()) {  				return; //not an internal resource  			} @@ -1675,7 +1680,7 @@ void Object::get_translatable_strings(List<String> *p_strings) const {  		String text = get(E.name); -		if (text == "") { +		if (text.is_empty()) {  			continue;  		} @@ -1833,8 +1838,6 @@ void Object::_construct_object(bool p_reference) {  	type_is_reference = p_reference;  	_instance_id = ObjectDB::add_instance(this); -	ClassDB::instance_get_native_extension_data(&_extension, &_extension_instance, this); -  #ifdef DEBUG_ENABLED  	_lock_index.init(1);  #endif diff --git a/core/object/object.h b/core/object/object.h index 102776a589..fc6e6a3660 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -132,10 +132,12 @@ enum PropertyUsageFlags {  	PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 26, // when loading, the resource for this property can be set at the end of loading  	PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 27, // For Object properties, instantiate them when creating in editor.  	PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 28, //for project or editor settings, show when basic settings are selected +	PROPERTY_USAGE_READ_ONLY = 1 << 29, // Mark a property as read-only in the inspector. +	PROPERTY_USAGE_ARRAY = 1 << 30, // Used in the inspector to group properties as elements of an array.  	PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK,  	PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED, -	PROPERTY_USAGE_NOEDITOR = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_NETWORK, +	PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_NETWORK,  };  #define ADD_SIGNAL(m_signal) ::ClassDB::add_signal(get_class_static(), m_signal) @@ -146,6 +148,10 @@ enum PropertyUsageFlags {  #define ADD_SUBGROUP(m_name, m_prefix) ::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix)  #define ADD_LINKED_PROPERTY(m_property, m_linked_property) ::ClassDB::add_linked_property(get_class_static(), m_property, m_linked_property) +#define ADD_ARRAY_COUNT(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, _scs_create(m_count_property_setter), _scs_create(m_count_property_getter), m_prefix) +#define ADD_ARRAY_COUNT_WITH_USAGE_FLAGS(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix, m_property_usage_flags) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, _scs_create(m_count_property_setter), _scs_create(m_count_property_getter), m_prefix, m_property_usage_flags) +#define ADD_ARRAY(m_array_path, m_prefix) ClassDB::add_property_array(get_class_static(), m_array_path, m_prefix) +  struct PropertyInfo {  	Variant::Type type = Variant::NIL;  	String name; @@ -278,7 +284,6 @@ struct ObjectNativeExtension {  	GDNativeExtensionClassCreateInstance create_instance;  	GDNativeExtensionClassFreeInstance free_instance; -	GDNativeExtensionClassObjectInstance set_object_instance;  	GDNativeExtensionClassGetVirtual get_virtual;  }; @@ -347,7 +352,7 @@ public:  	static String get_category_static() {                                                                                                        \  		String category = m_inherits::get_category_static();                                                                                     \  		if (_get_category != m_inherits::_get_category) {                                                                                        \ -			if (category != "") {                                                                                                                \ +			if (!category.is_empty()) {                                                                                                          \  				category += "/";                                                                                                                 \  			}                                                                                                                                    \  			category += _get_category();                                                                                                         \ @@ -397,7 +402,7 @@ protected:  		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;                                                          \ +		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()) {                                                                                     \ @@ -408,7 +413,7 @@ protected:  		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;                                                          \ +		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)) {                                                                                             \ @@ -420,7 +425,7 @@ protected:  		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;                                                     \ +		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) {                                                                                                                       \ @@ -441,7 +446,7 @@ protected:  		}                                                                                                                                        \  	}                                                                                                                                            \  	_FORCE_INLINE_ void (Object::*_get_notification() const)(int) {                                                                              \ -		return (void (Object::*)(int)) & m_class::_notification;                                                                                 \ +		return (void(Object::*)(int)) & m_class::_notification;                                                                                  \  	}                                                                                                                                            \  	virtual void _notificationv(int p_notification, bool p_reversed) override {                                                                  \  		if (!p_reversed) {                                                                                                                       \ diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 0fb8c7350c..8ec1a973e7 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -93,8 +93,8 @@ Dictionary Script::_get_script_constant_map() {  	Dictionary ret;  	Map<StringName, Variant> map;  	get_constants(&map); -	for (Map<StringName, Variant>::Element *E = map.front(); E; E = E->next()) { -		ret[E->key()] = E->value(); +	for (const KeyValue<StringName, Variant> &E : map) { +		ret[E.key] = E.value;  	}  	return ret;  } @@ -511,7 +511,7 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c  		Variant defval;  		if (script->get_property_default_value(E->key(), defval)) {  			//remove because it's the same as the default value -			if (defval == E) { +			if (defval == E->get()) {  				to_remove.push_back(E->key());  			}  		} @@ -549,7 +549,7 @@ void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name,  			}  		}  		if (!found) { -			properties.push_back(PropertyInfo(p_value.get_type(), p_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE)); +			properties.push_back(PropertyInfo(p_value.get_type(), p_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE));  		}  	} diff --git a/core/object/script_language.h b/core/object/script_language.h index 385bf79c1a..8d76cbf479 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -32,8 +32,8 @@  #define SCRIPT_LANGUAGE_H  #include "core/doc_data.h" -#include "core/io/multiplayer_api.h"  #include "core/io/resource.h" +#include "core/multiplayer/multiplayer.h"  #include "core/templates/map.h"  #include "core/templates/pair.h" @@ -159,7 +159,7 @@ public:  	virtual bool is_placeholder_fallback_enabled() const { return false; } -	virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const = 0; +	virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0;  	Script() {}  }; @@ -200,7 +200,7 @@ public:  	virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid);  	virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid); -	virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const = 0; +	virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0;  	virtual ScriptLanguage *get_language() = 0;  	virtual ~ScriptInstance(); @@ -419,7 +419,7 @@ public:  	virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr);  	virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = nullptr); -	virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const { return Vector<MultiplayerAPI::RPCConfig>(); } +	virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const { return Vector<Multiplayer::RPCConfig>(); }  	PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner);  	~PlaceHolderScriptInstance(); diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index b7d2bac96d..3459506860 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -32,6 +32,7 @@  #include "core/io/resource.h"  #include "core/os/os.h" +#include "core/templates/local_vector.h"  void UndoRedo::_discard_redo() {  	if (current_action == actions.size() - 1) { @@ -75,7 +76,7 @@ bool UndoRedo::_redo(bool p_execute) {  }  void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { -	uint32_t ticks = OS::get_singleton()->get_ticks_msec(); +	uint64_t ticks = OS::get_singleton()->get_ticks_msec();  	if (action_level == 0) {  		_discard_redo(); @@ -85,10 +86,17 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {  			current_action = actions.size() - 2;  			if (p_mode == MERGE_ENDS) { -				// Clear all do ops from last action, and delete all object references -				List<Operation>::Element *E = actions.write[current_action + 1].do_ops.front(); +				// Clear all do ops from last action if they are not forced kept +				LocalVector<List<Operation>::Element *> to_remove; +				for (List<Operation>::Element *E = actions.write[current_action + 1].do_ops.front(); E; E = E->next()) { +					if (!E->get().force_keep_in_merge_ends) { +						to_remove.push_back(E); +					} +				} -				while (E) { +				for (unsigned int i = 0; i < to_remove.size(); i++) { +					List<Operation>::Element *E = to_remove[i]; +					// Delete all object references  					if (E->get().type == Operation::TYPE_REFERENCE) {  						Object *obj = ObjectDB::get_instance(E->get().object); @@ -96,9 +104,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {  							memdelete(obj);  						}  					} - -					E = E->next(); -					actions.write[current_action + 1].do_ops.pop_front(); +					E->erase();  				}  			} @@ -117,6 +123,8 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {  	}  	action_level++; + +	force_keep_in_merge_ends = false;  }  void UndoRedo::add_do_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) { @@ -146,7 +154,7 @@ void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VAR  	ERR_FAIL_COND((current_action + 1) >= actions.size());  	// No undo if the merge mode is MERGE_ENDS -	if (merge_mode == MERGE_ENDS) { +	if (!force_keep_in_merge_ends && merge_mode == MERGE_ENDS) {  		return;  	} @@ -157,6 +165,7 @@ void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VAR  	}  	undo_op.type = Operation::TYPE_METHOD; +	undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends;  	undo_op.name = p_method;  	for (int i = 0; i < VARIANT_ARG_MAX; i++) { @@ -187,7 +196,7 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property,  	ERR_FAIL_COND((current_action + 1) >= actions.size());  	// No undo if the merge mode is MERGE_ENDS -	if (merge_mode == MERGE_ENDS) { +	if (!force_keep_in_merge_ends && merge_mode == MERGE_ENDS) {  		return;  	} @@ -198,6 +207,7 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property,  	}  	undo_op.type = Operation::TYPE_PROPERTY; +	undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends;  	undo_op.name = p_property;  	undo_op.args[0] = p_value;  	actions.write[current_action + 1].undo_ops.push_back(undo_op); @@ -223,7 +233,7 @@ void UndoRedo::add_undo_reference(Object *p_object) {  	ERR_FAIL_COND((current_action + 1) >= actions.size());  	// No undo if the merge mode is MERGE_ENDS -	if (merge_mode == MERGE_ENDS) { +	if (!force_keep_in_merge_ends && merge_mode == MERGE_ENDS) {  		return;  	} @@ -234,9 +244,24 @@ void UndoRedo::add_undo_reference(Object *p_object) {  	}  	undo_op.type = Operation::TYPE_REFERENCE; +	undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends;  	actions.write[current_action + 1].undo_ops.push_back(undo_op);  } +void UndoRedo::start_force_keep_in_merge_ends() { +	ERR_FAIL_COND(action_level <= 0); +	ERR_FAIL_COND((current_action + 1) >= actions.size()); + +	force_keep_in_merge_ends = true; +} + +void UndoRedo::end_force_keep_in_merge_ends() { +	ERR_FAIL_COND(action_level <= 0); +	ERR_FAIL_COND((current_action + 1) >= actions.size()); + +	force_keep_in_merge_ends = false; +} +  void UndoRedo::_pop_history_tail() {  	_discard_redo(); @@ -257,7 +282,7 @@ void UndoRedo::_pop_history_tail() {  		}  	} -	actions.remove(0); +	actions.remove_at(0);  	if (current_action >= 0) {  		current_action--;  	} @@ -538,6 +563,9 @@ void UndoRedo::_bind_methods() {  	ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &UndoRedo::add_do_reference);  	ClassDB::bind_method(D_METHOD("add_undo_reference", "object"), &UndoRedo::add_undo_reference); +	ClassDB::bind_method(D_METHOD("start_force_keep_in_merge_ends"), &UndoRedo::start_force_keep_in_merge_ends); +	ClassDB::bind_method(D_METHOD("end_force_keep_in_merge_ends"), &UndoRedo::end_force_keep_in_merge_ends); +  	ClassDB::bind_method(D_METHOD("get_history_count"), &UndoRedo::get_history_count);  	ClassDB::bind_method(D_METHOD("get_current_action"), &UndoRedo::get_current_action);  	ClassDB::bind_method(D_METHOD("get_action_name", "id"), &UndoRedo::get_action_name); diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h index d1ce252d86..a757d154e2 100644 --- a/core/object/undo_redo.h +++ b/core/object/undo_redo.h @@ -61,6 +61,7 @@ private:  		};  		Type type; +		bool force_keep_in_merge_ends;  		Ref<RefCounted> ref;  		ObjectID object;  		StringName name; @@ -76,6 +77,7 @@ private:  	Vector<Action> actions;  	int current_action = -1; +	bool force_keep_in_merge_ends = false;  	int action_level = 0;  	MergeMode merge_mode = MERGE_DISABLE;  	bool merging = false; @@ -109,6 +111,9 @@ public:  	void add_do_reference(Object *p_object);  	void add_undo_reference(Object *p_object); +	void start_force_keep_in_merge_ends(); +	void end_force_keep_in_merge_ends(); +  	bool is_committing_action() const;  	void commit_action(bool p_execute = true); diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp index 4c5f0b5220..fdc43da7a5 100644 --- a/core/os/keyboard.cpp +++ b/core/os/keyboard.cpp @@ -33,400 +33,399 @@  #include "core/os/os.h"  struct _KeyCodeText { -	int code; +	Key code;  	const char *text;  };  static const _KeyCodeText _keycodes[] = {  	/* clang-format off */ -		{KEY_ESCAPE                        ,"Escape"}, -		{KEY_TAB                           ,"Tab"}, -		{KEY_BACKTAB                       ,"BackTab"}, -		{KEY_BACKSPACE                     ,"BackSpace"}, -		{KEY_ENTER                         ,"Enter"}, -		{KEY_KP_ENTER                      ,"Kp Enter"}, -		{KEY_INSERT                        ,"Insert"}, -		{KEY_DELETE                        ,"Delete"}, -		{KEY_PAUSE                         ,"Pause"}, -		{KEY_PRINT                         ,"Print"}, -		{KEY_SYSREQ                        ,"SysReq"}, -		{KEY_CLEAR                         ,"Clear"}, -		{KEY_HOME                          ,"Home"}, -		{KEY_END                           ,"End"}, -		{KEY_LEFT                          ,"Left"}, -		{KEY_UP                            ,"Up"}, -		{KEY_RIGHT                         ,"Right"}, -		{KEY_DOWN                          ,"Down"}, -		{KEY_PAGEUP                        ,"PageUp"}, -		{KEY_PAGEDOWN                      ,"PageDown"}, -		{KEY_SHIFT                         ,"Shift"}, -		{KEY_CTRL                          ,"Ctrl"}, +	{Key::ESCAPE                ,"Escape"}, +	{Key::TAB                   ,"Tab"}, +	{Key::BACKTAB               ,"BackTab"}, +	{Key::BACKSPACE             ,"BackSpace"}, +	{Key::ENTER                 ,"Enter"}, +	{Key::KP_ENTER              ,"Kp Enter"}, +	{Key::INSERT                ,"Insert"}, +	{Key::KEY_DELETE            ,"Delete"}, +	{Key::PAUSE                 ,"Pause"}, +	{Key::PRINT                 ,"Print"}, +	{Key::SYSREQ                ,"SysReq"}, +	{Key::CLEAR                 ,"Clear"}, +	{Key::HOME                  ,"Home"}, +	{Key::END                   ,"End"}, +	{Key::LEFT                  ,"Left"}, +	{Key::UP                    ,"Up"}, +	{Key::RIGHT                 ,"Right"}, +	{Key::DOWN                  ,"Down"}, +	{Key::PAGEUP                ,"PageUp"}, +	{Key::PAGEDOWN              ,"PageDown"}, +	{Key::SHIFT                 ,"Shift"}, +	{Key::CTRL                  ,"Ctrl"},  #ifdef OSX_ENABLED -		{KEY_META                          ,"Command"}, +	{Key::META                  ,"Command"},  #else -		{KEY_META                          ,"Meta"}, +	{Key::META                  ,"Meta"},  #endif -		{KEY_ALT                           ,"Alt"}, -		{KEY_CAPSLOCK                      ,"CapsLock"}, -		{KEY_NUMLOCK                       ,"NumLock"}, -		{KEY_SCROLLLOCK                    ,"ScrollLock"}, -		{KEY_F1                            ,"F1"}, -		{KEY_F2                            ,"F2"}, -		{KEY_F3                            ,"F3"}, -		{KEY_F4                            ,"F4"}, -		{KEY_F5                            ,"F5"}, -		{KEY_F6                            ,"F6"}, -		{KEY_F7                            ,"F7"}, -		{KEY_F8                            ,"F8"}, -		{KEY_F9                            ,"F9"}, -		{KEY_F10                           ,"F10"}, -		{KEY_F11                           ,"F11"}, -		{KEY_F12                           ,"F12"}, -		{KEY_F13                           ,"F13"}, -		{KEY_F14                           ,"F14"}, -		{KEY_F15                           ,"F15"}, -		{KEY_F16                           ,"F16"}, -		{KEY_KP_MULTIPLY                   ,"Kp Multiply"}, -		{KEY_KP_DIVIDE                     ,"Kp Divide"}, -		{KEY_KP_SUBTRACT                   ,"Kp Subtract"}, -		{KEY_KP_PERIOD                     ,"Kp Period"}, -		{KEY_KP_ADD                        ,"Kp Add"}, -		{KEY_KP_0                          ,"Kp 0"}, -		{KEY_KP_1                          ,"Kp 1"}, -		{KEY_KP_2                          ,"Kp 2"}, -		{KEY_KP_3                          ,"Kp 3"}, -		{KEY_KP_4                          ,"Kp 4"}, -		{KEY_KP_5                          ,"Kp 5"}, -		{KEY_KP_6                          ,"Kp 6"}, -		{KEY_KP_7                          ,"Kp 7"}, -		{KEY_KP_8                          ,"Kp 8"}, -		{KEY_KP_9                          ,"Kp 9"}, -		{KEY_SUPER_L                       ,"Super L"}, -		{KEY_SUPER_R                       ,"Super R"}, -		{KEY_MENU                          ,"Menu"}, -		{KEY_HYPER_L                       ,"Hyper L"}, -		{KEY_HYPER_R                       ,"Hyper R"}, -		{KEY_HELP                          ,"Help"}, -		{KEY_DIRECTION_L                   ,"Direction L"}, -		{KEY_DIRECTION_R                   ,"Direction R"}, -		{KEY_BACK                          ,"Back"}, -		{KEY_FORWARD                       ,"Forward"}, -		{KEY_STOP                          ,"Stop"}, -		{KEY_REFRESH                       ,"Refresh"}, -		{KEY_VOLUMEDOWN                    ,"VolumeDown"}, -		{KEY_VOLUMEMUTE                    ,"VolumeMute"}, -		{KEY_VOLUMEUP                      ,"VolumeUp"}, -		{KEY_BASSBOOST                     ,"BassBoost"}, -		{KEY_BASSUP                        ,"BassUp"}, -		{KEY_BASSDOWN                      ,"BassDown"}, -		{KEY_TREBLEUP                      ,"TrebleUp"}, -		{KEY_TREBLEDOWN                    ,"TrebleDown"}, -		{KEY_MEDIAPLAY                     ,"MediaPlay"}, -		{KEY_MEDIASTOP                     ,"MediaStop"}, -		{KEY_MEDIAPREVIOUS                 ,"MediaPrevious"}, -		{KEY_MEDIANEXT                     ,"MediaNext"}, -		{KEY_MEDIARECORD                   ,"MediaRecord"}, -		{KEY_HOMEPAGE                      ,"HomePage"}, -		{KEY_FAVORITES                     ,"Favorites"}, -		{KEY_SEARCH                        ,"Search"}, -		{KEY_STANDBY                       ,"StandBy"}, -		{KEY_LAUNCHMAIL                    ,"LaunchMail"}, -		{KEY_LAUNCHMEDIA                   ,"LaunchMedia"}, -		{KEY_LAUNCH0                       ,"Launch0"}, -		{KEY_LAUNCH1                       ,"Launch1"}, -		{KEY_LAUNCH2                       ,"Launch2"}, -		{KEY_LAUNCH3                       ,"Launch3"}, -		{KEY_LAUNCH4                       ,"Launch4"}, -		{KEY_LAUNCH5                       ,"Launch5"}, -		{KEY_LAUNCH6                       ,"Launch6"}, -		{KEY_LAUNCH7                       ,"Launch7"}, -		{KEY_LAUNCH8                       ,"Launch8"}, -		{KEY_LAUNCH9                       ,"Launch9"}, -		{KEY_LAUNCHA                       ,"LaunchA"}, -		{KEY_LAUNCHB                       ,"LaunchB"}, -		{KEY_LAUNCHC                       ,"LaunchC"}, -		{KEY_LAUNCHD                       ,"LaunchD"}, -		{KEY_LAUNCHE                       ,"LaunchE"}, -		{KEY_LAUNCHF                       ,"LaunchF"}, - -		{KEY_UNKNOWN                       ,"Unknown"}, - -		{KEY_SPACE                         ,"Space"}, -		{KEY_EXCLAM                        ,"Exclam"}, -		{KEY_QUOTEDBL                      ,"QuoteDbl"}, -		{KEY_NUMBERSIGN                    ,"NumberSign"}, -		{KEY_DOLLAR                        ,"Dollar"}, -		{KEY_PERCENT                       ,"Percent"}, -		{KEY_AMPERSAND                     ,"Ampersand"}, -		{KEY_APOSTROPHE                    ,"Apostrophe"}, -		{KEY_PARENLEFT                     ,"ParenLeft"}, -		{KEY_PARENRIGHT                    ,"ParenRight"}, -		{KEY_ASTERISK                      ,"Asterisk"}, -		{KEY_PLUS                          ,"Plus"}, -		{KEY_COMMA                         ,"Comma"}, -		{KEY_MINUS                         ,"Minus"}, -		{KEY_PERIOD                        ,"Period"}, -		{KEY_SLASH                         ,"Slash"}, -		{KEY_0                             ,"0"}, -		{KEY_1                             ,"1"}, -		{KEY_2                             ,"2"}, -		{KEY_3                             ,"3"}, -		{KEY_4                             ,"4"}, -		{KEY_5                             ,"5"}, -		{KEY_6                             ,"6"}, -		{KEY_7                             ,"7"}, -		{KEY_8                             ,"8"}, -		{KEY_9                             ,"9"}, -		{KEY_COLON                         ,"Colon"}, -		{KEY_SEMICOLON                     ,"Semicolon"}, -		{KEY_LESS                          ,"Less"}, -		{KEY_EQUAL                         ,"Equal"}, -		{KEY_GREATER                       ,"Greater"}, -		{KEY_QUESTION                      ,"Question"}, -		{KEY_AT                            ,"At"}, -		{KEY_A                             ,"A"}, -		{KEY_B                             ,"B"}, -		{KEY_C                             ,"C"}, -		{KEY_D                             ,"D"}, -		{KEY_E                             ,"E"}, -		{KEY_F                             ,"F"}, -		{KEY_G                             ,"G"}, -		{KEY_H                             ,"H"}, -		{KEY_I                             ,"I"}, -		{KEY_J                             ,"J"}, -		{KEY_K                             ,"K"}, -		{KEY_L                             ,"L"}, -		{KEY_M                             ,"M"}, -		{KEY_N                             ,"N"}, -		{KEY_O                             ,"O"}, -		{KEY_P                             ,"P"}, -		{KEY_Q                             ,"Q"}, -		{KEY_R                             ,"R"}, -		{KEY_S                             ,"S"}, -		{KEY_T                             ,"T"}, -		{KEY_U                             ,"U"}, -		{KEY_V                             ,"V"}, -		{KEY_W                             ,"W"}, -		{KEY_X                             ,"X"}, -		{KEY_Y                             ,"Y"}, -		{KEY_Z                             ,"Z"}, -		{KEY_BRACKETLEFT                   ,"BracketLeft"}, -		{KEY_BACKSLASH                     ,"BackSlash"}, -		{KEY_BRACKETRIGHT                  ,"BracketRight"}, -		{KEY_ASCIICIRCUM                   ,"AsciiCircum"}, -		{KEY_UNDERSCORE                    ,"UnderScore"}, -		{KEY_QUOTELEFT                     ,"QuoteLeft"}, -		{KEY_BRACELEFT                     ,"BraceLeft"}, -		{KEY_BAR                           ,"Bar"}, -		{KEY_BRACERIGHT                    ,"BraceRight"}, -		{KEY_ASCIITILDE                    ,"AsciiTilde"}, -		{KEY_NOBREAKSPACE                  ,"NoBreakSpace"}, -		{KEY_EXCLAMDOWN                    ,"ExclamDown"}, -		{KEY_CENT                          ,"Cent"}, -		{KEY_STERLING                      ,"Sterling"}, -		{KEY_CURRENCY                      ,"Currency"}, -		{KEY_YEN                           ,"Yen"}, -		{KEY_BROKENBAR                     ,"BrokenBar"}, -		{KEY_SECTION                       ,"Section"}, -		{KEY_DIAERESIS                     ,"Diaeresis"}, -		{KEY_COPYRIGHT                     ,"Copyright"}, -		{KEY_ORDFEMININE                   ,"Ordfeminine"}, -		{KEY_GUILLEMOTLEFT                 ,"GuillemotLeft"}, -		{KEY_NOTSIGN                       ,"NotSign"}, -		{KEY_HYPHEN                        ,"Hyphen"}, -		{KEY_REGISTERED                    ,"Registered"}, -		{KEY_MACRON                        ,"Macron"}, -		{KEY_DEGREE                        ,"Degree"}, -		{KEY_PLUSMINUS                     ,"PlusMinus"}, -		{KEY_TWOSUPERIOR                   ,"TwoSuperior"}, -		{KEY_THREESUPERIOR                 ,"ThreeSuperior"}, -		{KEY_ACUTE                         ,"Acute"}, -		{KEY_MU                            ,"Mu"}, -		{KEY_PARAGRAPH                     ,"Paragraph"}, -		{KEY_PERIODCENTERED                ,"PeriodCentered"}, -		{KEY_CEDILLA                       ,"Cedilla"}, -		{KEY_ONESUPERIOR                   ,"OneSuperior"}, -		{KEY_MASCULINE                     ,"Masculine"}, -		{KEY_GUILLEMOTRIGHT                ,"GuillemotRight"}, -		{KEY_ONEQUARTER                    ,"OneQuarter"}, -		{KEY_ONEHALF                       ,"OneHalf"}, -		{KEY_THREEQUARTERS                 ,"ThreeQuarters"}, -		{KEY_QUESTIONDOWN                  ,"QuestionDown"}, -		{KEY_AGRAVE                        ,"Agrave"}, -		{KEY_AACUTE                        ,"Aacute"}, -		{KEY_ACIRCUMFLEX                   ,"AcircumFlex"}, -		{KEY_ATILDE                        ,"Atilde"}, -		{KEY_ADIAERESIS                    ,"Adiaeresis"}, -		{KEY_ARING                         ,"Aring"}, -		{KEY_AE                            ,"Ae"}, -		{KEY_CCEDILLA                      ,"Ccedilla"}, -		{KEY_EGRAVE                        ,"Egrave"}, -		{KEY_EACUTE                        ,"Eacute"}, -		{KEY_ECIRCUMFLEX                   ,"Ecircumflex"}, -		{KEY_EDIAERESIS                    ,"Ediaeresis"}, -		{KEY_IGRAVE                        ,"Igrave"}, -		{KEY_IACUTE                        ,"Iacute"}, -		{KEY_ICIRCUMFLEX                   ,"Icircumflex"}, -		{KEY_IDIAERESIS                    ,"Idiaeresis"}, -		{KEY_ETH                           ,"Eth"}, -		{KEY_NTILDE                        ,"Ntilde"}, -		{KEY_OGRAVE                        ,"Ograve"}, -		{KEY_OACUTE                        ,"Oacute"}, -		{KEY_OCIRCUMFLEX                   ,"Ocircumflex"}, -		{KEY_OTILDE                        ,"Otilde"}, -		{KEY_ODIAERESIS                    ,"Odiaeresis"}, -		{KEY_MULTIPLY                      ,"Multiply"}, -		{KEY_OOBLIQUE                      ,"Ooblique"}, -		{KEY_UGRAVE                        ,"Ugrave"}, -		{KEY_UACUTE                        ,"Uacute"}, -		{KEY_UCIRCUMFLEX                   ,"Ucircumflex"}, -		{KEY_UDIAERESIS                    ,"Udiaeresis"}, -		{KEY_YACUTE                        ,"Yacute"}, -		{KEY_THORN                         ,"Thorn"}, -		{KEY_SSHARP                        ,"Ssharp"}, - -		{KEY_DIVISION                      ,"Division"}, -		{KEY_YDIAERESIS                    ,"Ydiaeresis"}, -		{0                                 ,nullptr} +	{Key::ALT                   ,"Alt"}, +	{Key::CAPSLOCK              ,"CapsLock"}, +	{Key::NUMLOCK               ,"NumLock"}, +	{Key::SCROLLLOCK            ,"ScrollLock"}, +	{Key::F1                    ,"F1"}, +	{Key::F2                    ,"F2"}, +	{Key::F3                    ,"F3"}, +	{Key::F4                    ,"F4"}, +	{Key::F5                    ,"F5"}, +	{Key::F6                    ,"F6"}, +	{Key::F7                    ,"F7"}, +	{Key::F8                    ,"F8"}, +	{Key::F9                    ,"F9"}, +	{Key::F10                   ,"F10"}, +	{Key::F11                   ,"F11"}, +	{Key::F12                   ,"F12"}, +	{Key::F13                   ,"F13"}, +	{Key::F14                   ,"F14"}, +	{Key::F15                   ,"F15"}, +	{Key::F16                   ,"F16"}, +	{Key::KP_MULTIPLY           ,"Kp Multiply"}, +	{Key::KP_DIVIDE             ,"Kp Divide"}, +	{Key::KP_SUBTRACT           ,"Kp Subtract"}, +	{Key::KP_PERIOD             ,"Kp Period"}, +	{Key::KP_ADD                ,"Kp Add"}, +	{Key::KP_0                  ,"Kp 0"}, +	{Key::KP_1                  ,"Kp 1"}, +	{Key::KP_2                  ,"Kp 2"}, +	{Key::KP_3                  ,"Kp 3"}, +	{Key::KP_4                  ,"Kp 4"}, +	{Key::KP_5                  ,"Kp 5"}, +	{Key::KP_6                  ,"Kp 6"}, +	{Key::KP_7                  ,"Kp 7"}, +	{Key::KP_8                  ,"Kp 8"}, +	{Key::KP_9                  ,"Kp 9"}, +	{Key::SUPER_L               ,"Super L"}, +	{Key::SUPER_R               ,"Super R"}, +	{Key::MENU                  ,"Menu"}, +	{Key::HYPER_L               ,"Hyper L"}, +	{Key::HYPER_R               ,"Hyper R"}, +	{Key::HELP                  ,"Help"}, +	{Key::DIRECTION_L           ,"Direction L"}, +	{Key::DIRECTION_R           ,"Direction R"}, +	{Key::BACK                  ,"Back"}, +	{Key::FORWARD               ,"Forward"}, +	{Key::STOP                  ,"Stop"}, +	{Key::REFRESH               ,"Refresh"}, +	{Key::VOLUMEDOWN            ,"VolumeDown"}, +	{Key::VOLUMEMUTE            ,"VolumeMute"}, +	{Key::VOLUMEUP              ,"VolumeUp"}, +	{Key::BASSBOOST             ,"BassBoost"}, +	{Key::BASSUP                ,"BassUp"}, +	{Key::BASSDOWN              ,"BassDown"}, +	{Key::TREBLEUP              ,"TrebleUp"}, +	{Key::TREBLEDOWN            ,"TrebleDown"}, +	{Key::MEDIAPLAY             ,"MediaPlay"}, +	{Key::MEDIASTOP             ,"MediaStop"}, +	{Key::MEDIAPREVIOUS         ,"MediaPrevious"}, +	{Key::MEDIANEXT             ,"MediaNext"}, +	{Key::MEDIARECORD           ,"MediaRecord"}, +	{Key::HOMEPAGE              ,"HomePage"}, +	{Key::FAVORITES             ,"Favorites"}, +	{Key::SEARCH                ,"Search"}, +	{Key::STANDBY               ,"StandBy"}, +	{Key::LAUNCHMAIL            ,"LaunchMail"}, +	{Key::LAUNCHMEDIA           ,"LaunchMedia"}, +	{Key::LAUNCH0               ,"Launch0"}, +	{Key::LAUNCH1               ,"Launch1"}, +	{Key::LAUNCH2               ,"Launch2"}, +	{Key::LAUNCH3               ,"Launch3"}, +	{Key::LAUNCH4               ,"Launch4"}, +	{Key::LAUNCH5               ,"Launch5"}, +	{Key::LAUNCH6               ,"Launch6"}, +	{Key::LAUNCH7               ,"Launch7"}, +	{Key::LAUNCH8               ,"Launch8"}, +	{Key::LAUNCH9               ,"Launch9"}, +	{Key::LAUNCHA               ,"LaunchA"}, +	{Key::LAUNCHB               ,"LaunchB"}, +	{Key::LAUNCHC               ,"LaunchC"}, +	{Key::LAUNCHD               ,"LaunchD"}, +	{Key::LAUNCHE               ,"LaunchE"}, +	{Key::LAUNCHF               ,"LaunchF"}, +	{Key::UNKNOWN               ,"Unknown"}, +	{Key::SPACE                 ,"Space"}, +	{Key::EXCLAM                ,"Exclam"}, +	{Key::QUOTEDBL              ,"QuoteDbl"}, +	{Key::NUMBERSIGN            ,"NumberSign"}, +	{Key::DOLLAR                ,"Dollar"}, +	{Key::PERCENT               ,"Percent"}, +	{Key::AMPERSAND             ,"Ampersand"}, +	{Key::APOSTROPHE            ,"Apostrophe"}, +	{Key::PARENLEFT             ,"ParenLeft"}, +	{Key::PARENRIGHT            ,"ParenRight"}, +	{Key::ASTERISK              ,"Asterisk"}, +	{Key::PLUS                  ,"Plus"}, +	{Key::COMMA                 ,"Comma"}, +	{Key::MINUS                 ,"Minus"}, +	{Key::PERIOD                ,"Period"}, +	{Key::SLASH                 ,"Slash"}, +	{Key::KEY_0                 ,"0"}, +	{Key::KEY_1                 ,"1"}, +	{Key::KEY_2                 ,"2"}, +	{Key::KEY_3                 ,"3"}, +	{Key::KEY_4                 ,"4"}, +	{Key::KEY_5                 ,"5"}, +	{Key::KEY_6                 ,"6"}, +	{Key::KEY_7                 ,"7"}, +	{Key::KEY_8                 ,"8"}, +	{Key::KEY_9                 ,"9"}, +	{Key::COLON                 ,"Colon"}, +	{Key::SEMICOLON             ,"Semicolon"}, +	{Key::LESS                  ,"Less"}, +	{Key::EQUAL                 ,"Equal"}, +	{Key::GREATER               ,"Greater"}, +	{Key::QUESTION              ,"Question"}, +	{Key::AT                    ,"At"}, +	{Key::A                     ,"A"}, +	{Key::B                     ,"B"}, +	{Key::C                     ,"C"}, +	{Key::D                     ,"D"}, +	{Key::E                     ,"E"}, +	{Key::F                     ,"F"}, +	{Key::G                     ,"G"}, +	{Key::H                     ,"H"}, +	{Key::I                     ,"I"}, +	{Key::J                     ,"J"}, +	{Key::K                     ,"K"}, +	{Key::L                     ,"L"}, +	{Key::M                     ,"M"}, +	{Key::N                     ,"N"}, +	{Key::O                     ,"O"}, +	{Key::P                     ,"P"}, +	{Key::Q                     ,"Q"}, +	{Key::R                     ,"R"}, +	{Key::S                     ,"S"}, +	{Key::T                     ,"T"}, +	{Key::U                     ,"U"}, +	{Key::V                     ,"V"}, +	{Key::W                     ,"W"}, +	{Key::X                     ,"X"}, +	{Key::Y                     ,"Y"}, +	{Key::Z                     ,"Z"}, +	{Key::BRACKETLEFT           ,"BracketLeft"}, +	{Key::BACKSLASH             ,"BackSlash"}, +	{Key::BRACKETRIGHT          ,"BracketRight"}, +	{Key::ASCIICIRCUM           ,"AsciiCircum"}, +	{Key::UNDERSCORE            ,"UnderScore"}, +	{Key::QUOTELEFT             ,"QuoteLeft"}, +	{Key::BRACELEFT             ,"BraceLeft"}, +	{Key::BAR                   ,"Bar"}, +	{Key::BRACERIGHT            ,"BraceRight"}, +	{Key::ASCIITILDE            ,"AsciiTilde"}, +	{Key::NOBREAKSPACE          ,"NoBreakSpace"}, +	{Key::EXCLAMDOWN            ,"ExclamDown"}, +	{Key::CENT                  ,"Cent"}, +	{Key::STERLING              ,"Sterling"}, +	{Key::CURRENCY              ,"Currency"}, +	{Key::YEN                   ,"Yen"}, +	{Key::BROKENBAR             ,"BrokenBar"}, +	{Key::SECTION               ,"Section"}, +	{Key::DIAERESIS             ,"Diaeresis"}, +	{Key::COPYRIGHT             ,"Copyright"}, +	{Key::ORDFEMININE           ,"Ordfeminine"}, +	{Key::GUILLEMOTLEFT         ,"GuillemotLeft"}, +	{Key::NOTSIGN               ,"NotSign"}, +	{Key::HYPHEN                ,"Hyphen"}, +	{Key::KEY_REGISTERED        ,"Registered"}, +	{Key::MACRON                ,"Macron"}, +	{Key::DEGREE                ,"Degree"}, +	{Key::PLUSMINUS             ,"PlusMinus"}, +	{Key::TWOSUPERIOR           ,"TwoSuperior"}, +	{Key::THREESUPERIOR         ,"ThreeSuperior"}, +	{Key::ACUTE                 ,"Acute"}, +	{Key::MU                    ,"Mu"}, +	{Key::PARAGRAPH             ,"Paragraph"}, +	{Key::PERIODCENTERED        ,"PeriodCentered"}, +	{Key::CEDILLA               ,"Cedilla"}, +	{Key::ONESUPERIOR           ,"OneSuperior"}, +	{Key::MASCULINE             ,"Masculine"}, +	{Key::GUILLEMOTRIGHT        ,"GuillemotRight"}, +	{Key::ONEQUARTER            ,"OneQuarter"}, +	{Key::ONEHALF               ,"OneHalf"}, +	{Key::THREEQUARTERS         ,"ThreeQuarters"}, +	{Key::QUESTIONDOWN          ,"QuestionDown"}, +	{Key::AGRAVE                ,"Agrave"}, +	{Key::AACUTE                ,"Aacute"}, +	{Key::ACIRCUMFLEX           ,"AcircumFlex"}, +	{Key::ATILDE                ,"Atilde"}, +	{Key::ADIAERESIS            ,"Adiaeresis"}, +	{Key::ARING                 ,"Aring"}, +	{Key::AE                    ,"Ae"}, +	{Key::CCEDILLA              ,"Ccedilla"}, +	{Key::EGRAVE                ,"Egrave"}, +	{Key::EACUTE                ,"Eacute"}, +	{Key::ECIRCUMFLEX           ,"Ecircumflex"}, +	{Key::EDIAERESIS            ,"Ediaeresis"}, +	{Key::IGRAVE                ,"Igrave"}, +	{Key::IACUTE                ,"Iacute"}, +	{Key::ICIRCUMFLEX           ,"Icircumflex"}, +	{Key::IDIAERESIS            ,"Idiaeresis"}, +	{Key::ETH                   ,"Eth"}, +	{Key::NTILDE                ,"Ntilde"}, +	{Key::OGRAVE                ,"Ograve"}, +	{Key::OACUTE                ,"Oacute"}, +	{Key::OCIRCUMFLEX           ,"Ocircumflex"}, +	{Key::OTILDE                ,"Otilde"}, +	{Key::ODIAERESIS            ,"Odiaeresis"}, +	{Key::MULTIPLY              ,"Multiply"}, +	{Key::OOBLIQUE              ,"Ooblique"}, +	{Key::UGRAVE                ,"Ugrave"}, +	{Key::UACUTE                ,"Uacute"}, +	{Key::UCIRCUMFLEX           ,"Ucircumflex"}, +	{Key::UDIAERESIS            ,"Udiaeresis"}, +	{Key::YACUTE                ,"Yacute"}, +	{Key::THORN                 ,"Thorn"}, +	{Key::SSHARP                ,"Ssharp"}, +	{Key::DIVISION              ,"Division"}, +	{Key::YDIAERESIS            ,"Ydiaeresis"}, +	{Key::NONE                  ,nullptr}  	/* clang-format on */  }; -bool keycode_has_unicode(uint32_t p_keycode) { +bool keycode_has_unicode(Key p_keycode) {  	switch (p_keycode) { -		case KEY_ESCAPE: -		case KEY_TAB: -		case KEY_BACKTAB: -		case KEY_BACKSPACE: -		case KEY_ENTER: -		case KEY_KP_ENTER: -		case KEY_INSERT: -		case KEY_DELETE: -		case KEY_PAUSE: -		case KEY_PRINT: -		case KEY_SYSREQ: -		case KEY_CLEAR: -		case KEY_HOME: -		case KEY_END: -		case KEY_LEFT: -		case KEY_UP: -		case KEY_RIGHT: -		case KEY_DOWN: -		case KEY_PAGEUP: -		case KEY_PAGEDOWN: -		case KEY_SHIFT: -		case KEY_CTRL: -		case KEY_META: -		case KEY_ALT: -		case KEY_CAPSLOCK: -		case KEY_NUMLOCK: -		case KEY_SCROLLLOCK: -		case KEY_F1: -		case KEY_F2: -		case KEY_F3: -		case KEY_F4: -		case KEY_F5: -		case KEY_F6: -		case KEY_F7: -		case KEY_F8: -		case KEY_F9: -		case KEY_F10: -		case KEY_F11: -		case KEY_F12: -		case KEY_F13: -		case KEY_F14: -		case KEY_F15: -		case KEY_F16: -		case KEY_SUPER_L: -		case KEY_SUPER_R: -		case KEY_MENU: -		case KEY_HYPER_L: -		case KEY_HYPER_R: -		case KEY_HELP: -		case KEY_DIRECTION_L: -		case KEY_DIRECTION_R: -		case KEY_BACK: -		case KEY_FORWARD: -		case KEY_STOP: -		case KEY_REFRESH: -		case KEY_VOLUMEDOWN: -		case KEY_VOLUMEMUTE: -		case KEY_VOLUMEUP: -		case KEY_BASSBOOST: -		case KEY_BASSUP: -		case KEY_BASSDOWN: -		case KEY_TREBLEUP: -		case KEY_TREBLEDOWN: -		case KEY_MEDIAPLAY: -		case KEY_MEDIASTOP: -		case KEY_MEDIAPREVIOUS: -		case KEY_MEDIANEXT: -		case KEY_MEDIARECORD: -		case KEY_HOMEPAGE: -		case KEY_FAVORITES: -		case KEY_SEARCH: -		case KEY_STANDBY: -		case KEY_OPENURL: -		case KEY_LAUNCHMAIL: -		case KEY_LAUNCHMEDIA: -		case KEY_LAUNCH0: -		case KEY_LAUNCH1: -		case KEY_LAUNCH2: -		case KEY_LAUNCH3: -		case KEY_LAUNCH4: -		case KEY_LAUNCH5: -		case KEY_LAUNCH6: -		case KEY_LAUNCH7: -		case KEY_LAUNCH8: -		case KEY_LAUNCH9: -		case KEY_LAUNCHA: -		case KEY_LAUNCHB: -		case KEY_LAUNCHC: -		case KEY_LAUNCHD: -		case KEY_LAUNCHE: -		case KEY_LAUNCHF: +		case Key::ESCAPE: +		case Key::TAB: +		case Key::BACKTAB: +		case Key::BACKSPACE: +		case Key::ENTER: +		case Key::KP_ENTER: +		case Key::INSERT: +		case Key::KEY_DELETE: +		case Key::PAUSE: +		case Key::PRINT: +		case Key::SYSREQ: +		case Key::CLEAR: +		case Key::HOME: +		case Key::END: +		case Key::LEFT: +		case Key::UP: +		case Key::RIGHT: +		case Key::DOWN: +		case Key::PAGEUP: +		case Key::PAGEDOWN: +		case Key::SHIFT: +		case Key::CTRL: +		case Key::META: +		case Key::ALT: +		case Key::CAPSLOCK: +		case Key::NUMLOCK: +		case Key::SCROLLLOCK: +		case Key::F1: +		case Key::F2: +		case Key::F3: +		case Key::F4: +		case Key::F5: +		case Key::F6: +		case Key::F7: +		case Key::F8: +		case Key::F9: +		case Key::F10: +		case Key::F11: +		case Key::F12: +		case Key::F13: +		case Key::F14: +		case Key::F15: +		case Key::F16: +		case Key::SUPER_L: +		case Key::SUPER_R: +		case Key::MENU: +		case Key::HYPER_L: +		case Key::HYPER_R: +		case Key::HELP: +		case Key::DIRECTION_L: +		case Key::DIRECTION_R: +		case Key::BACK: +		case Key::FORWARD: +		case Key::STOP: +		case Key::REFRESH: +		case Key::VOLUMEDOWN: +		case Key::VOLUMEMUTE: +		case Key::VOLUMEUP: +		case Key::BASSBOOST: +		case Key::BASSUP: +		case Key::BASSDOWN: +		case Key::TREBLEUP: +		case Key::TREBLEDOWN: +		case Key::MEDIAPLAY: +		case Key::MEDIASTOP: +		case Key::MEDIAPREVIOUS: +		case Key::MEDIANEXT: +		case Key::MEDIARECORD: +		case Key::HOMEPAGE: +		case Key::FAVORITES: +		case Key::SEARCH: +		case Key::STANDBY: +		case Key::OPENURL: +		case Key::LAUNCHMAIL: +		case Key::LAUNCHMEDIA: +		case Key::LAUNCH0: +		case Key::LAUNCH1: +		case Key::LAUNCH2: +		case Key::LAUNCH3: +		case Key::LAUNCH4: +		case Key::LAUNCH5: +		case Key::LAUNCH6: +		case Key::LAUNCH7: +		case Key::LAUNCH8: +		case Key::LAUNCH9: +		case Key::LAUNCHA: +		case Key::LAUNCHB: +		case Key::LAUNCHC: +		case Key::LAUNCHD: +		case Key::LAUNCHE: +		case Key::LAUNCHF:  			return false; +		default: { +		}  	}  	return true;  } -String keycode_get_string(uint32_t p_code) { +String keycode_get_string(Key p_code) {  	String codestr; -	if (p_code & KEY_MASK_SHIFT) { -		codestr += find_keycode_name(KEY_SHIFT); +	if ((p_code & KeyModifierMask::SHIFT) != Key::NONE) { +		codestr += find_keycode_name(Key::SHIFT);  		codestr += "+";  	} -	if (p_code & KEY_MASK_ALT) { -		codestr += find_keycode_name(KEY_ALT); +	if ((p_code & KeyModifierMask::ALT) != Key::NONE) { +		codestr += find_keycode_name(Key::ALT);  		codestr += "+";  	} -	if (p_code & KEY_MASK_CTRL) { -		codestr += find_keycode_name(KEY_CTRL); +	if ((p_code & KeyModifierMask::CTRL) != Key::NONE) { +		codestr += find_keycode_name(Key::CTRL);  		codestr += "+";  	} -	if (p_code & KEY_MASK_META) { -		codestr += find_keycode_name(KEY_META); +	if ((p_code & KeyModifierMask::META) != Key::NONE) { +		codestr += find_keycode_name(Key::META);  		codestr += "+";  	} -	p_code &= KEY_CODE_MASK; +	p_code &= KeyModifierMask::CODE_MASK;  	const _KeyCodeText *kct = &_keycodes[0];  	while (kct->text) { -		if (kct->code == (int)p_code) { +		if (kct->code == p_code) {  			codestr += kct->text;  			return codestr;  		}  		kct++;  	} -	codestr += String::chr(p_code); +	codestr += String::chr((char32_t)p_code);  	return codestr;  } -int find_keycode(const String &p_code) { +Key find_keycode(const String &p_code) {  	const _KeyCodeText *kct = &_keycodes[0];  	while (kct->text) { @@ -436,10 +435,10 @@ int find_keycode(const String &p_code) {  		kct++;  	} -	return 0; +	return Key::NONE;  } -const char *find_keycode_name(int p_keycode) { +const char *find_keycode_name(Key p_keycode) {  	const _KeyCodeText *kct = &_keycodes[0];  	while (kct->text) { @@ -464,7 +463,7 @@ int keycode_get_count() {  }  int keycode_get_value_by_index(int p_index) { -	return _keycodes[p_index].code; +	return (int)_keycodes[p_index].code;  }  const char *keycode_get_name_by_index(int p_index) { diff --git a/core/os/keyboard.h b/core/os/keyboard.h index 52174432d9..c780099553 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -33,148 +33,142 @@  #include "core/string/ustring.h" -/* -	Special Key: - -	The strategy here is similar to the one used by toolkits, -	which consists in leaving the 24 bits unicode range for printable -	characters, and use the upper 8 bits for special keys and -	modifiers. This way everything (char/keycode) can fit nicely in one 32 bits unsigned integer. -*/ -enum { -	SPKEY = (1 << 24) -}; - -enum Key { -	KEY_NONE = 0, +enum class Key { +	NONE = 0, +	// Special key: The strategy here is similar to the one used by toolkits, +	// which consists in leaving the 24 bits unicode range for printable +	// characters, and use the upper 8 bits for special keys and modifiers. +	// This way everything (char/keycode) can fit nicely in one 32-bit +	// integer (the enum's underlying type is `int` by default). +	SPECIAL = (1 << 24),  	/* CURSOR/FUNCTION/BROWSER/MULTIMEDIA/MISC KEYS */ -	KEY_ESCAPE = SPKEY | 0x01, -	KEY_TAB = SPKEY | 0x02, -	KEY_BACKTAB = SPKEY | 0x03, -	KEY_BACKSPACE = SPKEY | 0x04, -	KEY_ENTER = SPKEY | 0x05, -	KEY_KP_ENTER = SPKEY | 0x06, -	KEY_INSERT = SPKEY | 0x07, -	KEY_DELETE = SPKEY | 0x08, -	KEY_PAUSE = SPKEY | 0x09, -	KEY_PRINT = SPKEY | 0x0A, -	KEY_SYSREQ = SPKEY | 0x0B, -	KEY_CLEAR = SPKEY | 0x0C, -	KEY_HOME = SPKEY | 0x0D, -	KEY_END = SPKEY | 0x0E, -	KEY_LEFT = SPKEY | 0x0F, -	KEY_UP = SPKEY | 0x10, -	KEY_RIGHT = SPKEY | 0x11, -	KEY_DOWN = SPKEY | 0x12, -	KEY_PAGEUP = SPKEY | 0x13, -	KEY_PAGEDOWN = SPKEY | 0x14, -	KEY_SHIFT = SPKEY | 0x15, -	KEY_CTRL = SPKEY | 0x16, -	KEY_META = SPKEY | 0x17, -	KEY_ALT = SPKEY | 0x18, -	KEY_CAPSLOCK = SPKEY | 0x19, -	KEY_NUMLOCK = SPKEY | 0x1A, -	KEY_SCROLLLOCK = SPKEY | 0x1B, -	KEY_F1 = SPKEY | 0x1C, -	KEY_F2 = SPKEY | 0x1D, -	KEY_F3 = SPKEY | 0x1E, -	KEY_F4 = SPKEY | 0x1F, -	KEY_F5 = SPKEY | 0x20, -	KEY_F6 = SPKEY | 0x21, -	KEY_F7 = SPKEY | 0x22, -	KEY_F8 = SPKEY | 0x23, -	KEY_F9 = SPKEY | 0x24, -	KEY_F10 = SPKEY | 0x25, -	KEY_F11 = SPKEY | 0x26, -	KEY_F12 = SPKEY | 0x27, -	KEY_F13 = SPKEY | 0x28, -	KEY_F14 = SPKEY | 0x29, -	KEY_F15 = SPKEY | 0x2A, -	KEY_F16 = SPKEY | 0x2B, -	KEY_KP_MULTIPLY = SPKEY | 0x81, -	KEY_KP_DIVIDE = SPKEY | 0x82, -	KEY_KP_SUBTRACT = SPKEY | 0x83, -	KEY_KP_PERIOD = SPKEY | 0x84, -	KEY_KP_ADD = SPKEY | 0x85, -	KEY_KP_0 = SPKEY | 0x86, -	KEY_KP_1 = SPKEY | 0x87, -	KEY_KP_2 = SPKEY | 0x88, -	KEY_KP_3 = SPKEY | 0x89, -	KEY_KP_4 = SPKEY | 0x8A, -	KEY_KP_5 = SPKEY | 0x8B, -	KEY_KP_6 = SPKEY | 0x8C, -	KEY_KP_7 = SPKEY | 0x8D, -	KEY_KP_8 = SPKEY | 0x8E, -	KEY_KP_9 = SPKEY | 0x8F, -	KEY_SUPER_L = SPKEY | 0x2C, -	KEY_SUPER_R = SPKEY | 0x2D, -	KEY_MENU = SPKEY | 0x2E, -	KEY_HYPER_L = SPKEY | 0x2F, -	KEY_HYPER_R = SPKEY | 0x30, -	KEY_HELP = SPKEY | 0x31, -	KEY_DIRECTION_L = SPKEY | 0x32, -	KEY_DIRECTION_R = SPKEY | 0x33, -	KEY_BACK = SPKEY | 0x40, -	KEY_FORWARD = SPKEY | 0x41, -	KEY_STOP = SPKEY | 0x42, -	KEY_REFRESH = SPKEY | 0x43, -	KEY_VOLUMEDOWN = SPKEY | 0x44, -	KEY_VOLUMEMUTE = SPKEY | 0x45, -	KEY_VOLUMEUP = SPKEY | 0x46, -	KEY_BASSBOOST = SPKEY | 0x47, -	KEY_BASSUP = SPKEY | 0x48, -	KEY_BASSDOWN = SPKEY | 0x49, -	KEY_TREBLEUP = SPKEY | 0x4A, -	KEY_TREBLEDOWN = SPKEY | 0x4B, -	KEY_MEDIAPLAY = SPKEY | 0x4C, -	KEY_MEDIASTOP = SPKEY | 0x4D, -	KEY_MEDIAPREVIOUS = SPKEY | 0x4E, -	KEY_MEDIANEXT = SPKEY | 0x4F, -	KEY_MEDIARECORD = SPKEY | 0x50, -	KEY_HOMEPAGE = SPKEY | 0x51, -	KEY_FAVORITES = SPKEY | 0x52, -	KEY_SEARCH = SPKEY | 0x53, -	KEY_STANDBY = SPKEY | 0x54, -	KEY_OPENURL = SPKEY | 0x55, -	KEY_LAUNCHMAIL = SPKEY | 0x56, -	KEY_LAUNCHMEDIA = SPKEY | 0x57, -	KEY_LAUNCH0 = SPKEY | 0x58, -	KEY_LAUNCH1 = SPKEY | 0x59, -	KEY_LAUNCH2 = SPKEY | 0x5A, -	KEY_LAUNCH3 = SPKEY | 0x5B, -	KEY_LAUNCH4 = SPKEY | 0x5C, -	KEY_LAUNCH5 = SPKEY | 0x5D, -	KEY_LAUNCH6 = SPKEY | 0x5E, -	KEY_LAUNCH7 = SPKEY | 0x5F, -	KEY_LAUNCH8 = SPKEY | 0x60, -	KEY_LAUNCH9 = SPKEY | 0x61, -	KEY_LAUNCHA = SPKEY | 0x62, -	KEY_LAUNCHB = SPKEY | 0x63, -	KEY_LAUNCHC = SPKEY | 0x64, -	KEY_LAUNCHD = SPKEY | 0x65, -	KEY_LAUNCHE = SPKEY | 0x66, -	KEY_LAUNCHF = SPKEY | 0x67, +	ESCAPE = SPECIAL | 0x01, +	TAB = SPECIAL | 0x02, +	BACKTAB = SPECIAL | 0x03, +	BACKSPACE = SPECIAL | 0x04, +	ENTER = SPECIAL | 0x05, +	KP_ENTER = SPECIAL | 0x06, +	INSERT = SPECIAL | 0x07, +	KEY_DELETE = SPECIAL | 0x08, // "DELETE" is a reserved word on Windows. +	PAUSE = SPECIAL | 0x09, +	PRINT = SPECIAL | 0x0A, +	SYSREQ = SPECIAL | 0x0B, +	CLEAR = SPECIAL | 0x0C, +	HOME = SPECIAL | 0x0D, +	END = SPECIAL | 0x0E, +	LEFT = SPECIAL | 0x0F, +	UP = SPECIAL | 0x10, +	RIGHT = SPECIAL | 0x11, +	DOWN = SPECIAL | 0x12, +	PAGEUP = SPECIAL | 0x13, +	PAGEDOWN = SPECIAL | 0x14, +	SHIFT = SPECIAL | 0x15, +	CTRL = SPECIAL | 0x16, +	META = SPECIAL | 0x17, +	ALT = SPECIAL | 0x18, +	CAPSLOCK = SPECIAL | 0x19, +	NUMLOCK = SPECIAL | 0x1A, +	SCROLLLOCK = SPECIAL | 0x1B, +	F1 = SPECIAL | 0x1C, +	F2 = SPECIAL | 0x1D, +	F3 = SPECIAL | 0x1E, +	F4 = SPECIAL | 0x1F, +	F5 = SPECIAL | 0x20, +	F6 = SPECIAL | 0x21, +	F7 = SPECIAL | 0x22, +	F8 = SPECIAL | 0x23, +	F9 = SPECIAL | 0x24, +	F10 = SPECIAL | 0x25, +	F11 = SPECIAL | 0x26, +	F12 = SPECIAL | 0x27, +	F13 = SPECIAL | 0x28, +	F14 = SPECIAL | 0x29, +	F15 = SPECIAL | 0x2A, +	F16 = SPECIAL | 0x2B, +	KP_MULTIPLY = SPECIAL | 0x81, +	KP_DIVIDE = SPECIAL | 0x82, +	KP_SUBTRACT = SPECIAL | 0x83, +	KP_PERIOD = SPECIAL | 0x84, +	KP_ADD = SPECIAL | 0x85, +	KP_0 = SPECIAL | 0x86, +	KP_1 = SPECIAL | 0x87, +	KP_2 = SPECIAL | 0x88, +	KP_3 = SPECIAL | 0x89, +	KP_4 = SPECIAL | 0x8A, +	KP_5 = SPECIAL | 0x8B, +	KP_6 = SPECIAL | 0x8C, +	KP_7 = SPECIAL | 0x8D, +	KP_8 = SPECIAL | 0x8E, +	KP_9 = SPECIAL | 0x8F, +	SUPER_L = SPECIAL | 0x2C, +	SUPER_R = SPECIAL | 0x2D, +	MENU = SPECIAL | 0x2E, +	HYPER_L = SPECIAL | 0x2F, +	HYPER_R = SPECIAL | 0x30, +	HELP = SPECIAL | 0x31, +	DIRECTION_L = SPECIAL | 0x32, +	DIRECTION_R = SPECIAL | 0x33, +	BACK = SPECIAL | 0x40, +	FORWARD = SPECIAL | 0x41, +	STOP = SPECIAL | 0x42, +	REFRESH = SPECIAL | 0x43, +	VOLUMEDOWN = SPECIAL | 0x44, +	VOLUMEMUTE = SPECIAL | 0x45, +	VOLUMEUP = SPECIAL | 0x46, +	BASSBOOST = SPECIAL | 0x47, +	BASSUP = SPECIAL | 0x48, +	BASSDOWN = SPECIAL | 0x49, +	TREBLEUP = SPECIAL | 0x4A, +	TREBLEDOWN = SPECIAL | 0x4B, +	MEDIAPLAY = SPECIAL | 0x4C, +	MEDIASTOP = SPECIAL | 0x4D, +	MEDIAPREVIOUS = SPECIAL | 0x4E, +	MEDIANEXT = SPECIAL | 0x4F, +	MEDIARECORD = SPECIAL | 0x50, +	HOMEPAGE = SPECIAL | 0x51, +	FAVORITES = SPECIAL | 0x52, +	SEARCH = SPECIAL | 0x53, +	STANDBY = SPECIAL | 0x54, +	OPENURL = SPECIAL | 0x55, +	LAUNCHMAIL = SPECIAL | 0x56, +	LAUNCHMEDIA = SPECIAL | 0x57, +	LAUNCH0 = SPECIAL | 0x58, +	LAUNCH1 = SPECIAL | 0x59, +	LAUNCH2 = SPECIAL | 0x5A, +	LAUNCH3 = SPECIAL | 0x5B, +	LAUNCH4 = SPECIAL | 0x5C, +	LAUNCH5 = SPECIAL | 0x5D, +	LAUNCH6 = SPECIAL | 0x5E, +	LAUNCH7 = SPECIAL | 0x5F, +	LAUNCH8 = SPECIAL | 0x60, +	LAUNCH9 = SPECIAL | 0x61, +	LAUNCHA = SPECIAL | 0x62, +	LAUNCHB = SPECIAL | 0x63, +	LAUNCHC = SPECIAL | 0x64, +	LAUNCHD = SPECIAL | 0x65, +	LAUNCHE = SPECIAL | 0x66, +	LAUNCHF = SPECIAL | 0x67, -	KEY_UNKNOWN = SPKEY | 0xFFFFFF, +	UNKNOWN = SPECIAL | 0xFFFFFF,  	/* PRINTABLE LATIN 1 CODES */ -	KEY_SPACE = 0x0020, -	KEY_EXCLAM = 0x0021, -	KEY_QUOTEDBL = 0x0022, -	KEY_NUMBERSIGN = 0x0023, -	KEY_DOLLAR = 0x0024, -	KEY_PERCENT = 0x0025, -	KEY_AMPERSAND = 0x0026, -	KEY_APOSTROPHE = 0x0027, -	KEY_PARENLEFT = 0x0028, -	KEY_PARENRIGHT = 0x0029, -	KEY_ASTERISK = 0x002A, -	KEY_PLUS = 0x002B, -	KEY_COMMA = 0x002C, -	KEY_MINUS = 0x002D, -	KEY_PERIOD = 0x002E, -	KEY_SLASH = 0x002F, +	SPACE = 0x0020, +	EXCLAM = 0x0021, +	QUOTEDBL = 0x0022, +	NUMBERSIGN = 0x0023, +	DOLLAR = 0x0024, +	PERCENT = 0x0025, +	AMPERSAND = 0x0026, +	APOSTROPHE = 0x0027, +	PARENLEFT = 0x0028, +	PARENRIGHT = 0x0029, +	ASTERISK = 0x002A, +	PLUS = 0x002B, +	COMMA = 0x002C, +	MINUS = 0x002D, +	PERIOD = 0x002E, +	SLASH = 0x002F,  	KEY_0 = 0x0030,  	KEY_1 = 0x0031,  	KEY_2 = 0x0032, @@ -185,134 +179,133 @@ enum Key {  	KEY_7 = 0x0037,  	KEY_8 = 0x0038,  	KEY_9 = 0x0039, -	KEY_COLON = 0x003A, -	KEY_SEMICOLON = 0x003B, -	KEY_LESS = 0x003C, -	KEY_EQUAL = 0x003D, -	KEY_GREATER = 0x003E, -	KEY_QUESTION = 0x003F, -	KEY_AT = 0x0040, -	KEY_A = 0x0041, -	KEY_B = 0x0042, -	KEY_C = 0x0043, -	KEY_D = 0x0044, -	KEY_E = 0x0045, -	KEY_F = 0x0046, -	KEY_G = 0x0047, -	KEY_H = 0x0048, -	KEY_I = 0x0049, -	KEY_J = 0x004A, -	KEY_K = 0x004B, -	KEY_L = 0x004C, -	KEY_M = 0x004D, -	KEY_N = 0x004E, -	KEY_O = 0x004F, -	KEY_P = 0x0050, -	KEY_Q = 0x0051, -	KEY_R = 0x0052, -	KEY_S = 0x0053, -	KEY_T = 0x0054, -	KEY_U = 0x0055, -	KEY_V = 0x0056, -	KEY_W = 0x0057, -	KEY_X = 0x0058, -	KEY_Y = 0x0059, -	KEY_Z = 0x005A, -	KEY_BRACKETLEFT = 0x005B, -	KEY_BACKSLASH = 0x005C, -	KEY_BRACKETRIGHT = 0x005D, -	KEY_ASCIICIRCUM = 0x005E, -	KEY_UNDERSCORE = 0x005F, -	KEY_QUOTELEFT = 0x0060, -	KEY_BRACELEFT = 0x007B, -	KEY_BAR = 0x007C, -	KEY_BRACERIGHT = 0x007D, -	KEY_ASCIITILDE = 0x007E, -	KEY_NOBREAKSPACE = 0x00A0, -	KEY_EXCLAMDOWN = 0x00A1, -	KEY_CENT = 0x00A2, -	KEY_STERLING = 0x00A3, -	KEY_CURRENCY = 0x00A4, -	KEY_YEN = 0x00A5, -	KEY_BROKENBAR = 0x00A6, -	KEY_SECTION = 0x00A7, -	KEY_DIAERESIS = 0x00A8, -	KEY_COPYRIGHT = 0x00A9, -	KEY_ORDFEMININE = 0x00AA, -	KEY_GUILLEMOTLEFT = 0x00AB, -	KEY_NOTSIGN = 0x00AC, -	KEY_HYPHEN = 0x00AD, -	KEY_REGISTERED = 0x00AE, -	KEY_MACRON = 0x00AF, -	KEY_DEGREE = 0x00B0, -	KEY_PLUSMINUS = 0x00B1, -	KEY_TWOSUPERIOR = 0x00B2, -	KEY_THREESUPERIOR = 0x00B3, -	KEY_ACUTE = 0x00B4, -	KEY_MU = 0x00B5, -	KEY_PARAGRAPH = 0x00B6, -	KEY_PERIODCENTERED = 0x00B7, -	KEY_CEDILLA = 0x00B8, -	KEY_ONESUPERIOR = 0x00B9, -	KEY_MASCULINE = 0x00BA, -	KEY_GUILLEMOTRIGHT = 0x00BB, -	KEY_ONEQUARTER = 0x00BC, -	KEY_ONEHALF = 0x00BD, -	KEY_THREEQUARTERS = 0x00BE, -	KEY_QUESTIONDOWN = 0x00BF, -	KEY_AGRAVE = 0x00C0, -	KEY_AACUTE = 0x00C1, -	KEY_ACIRCUMFLEX = 0x00C2, -	KEY_ATILDE = 0x00C3, -	KEY_ADIAERESIS = 0x00C4, -	KEY_ARING = 0x00C5, -	KEY_AE = 0x00C6, -	KEY_CCEDILLA = 0x00C7, -	KEY_EGRAVE = 0x00C8, -	KEY_EACUTE = 0x00C9, -	KEY_ECIRCUMFLEX = 0x00CA, -	KEY_EDIAERESIS = 0x00CB, -	KEY_IGRAVE = 0x00CC, -	KEY_IACUTE = 0x00CD, -	KEY_ICIRCUMFLEX = 0x00CE, -	KEY_IDIAERESIS = 0x00CF, -	KEY_ETH = 0x00D0, -	KEY_NTILDE = 0x00D1, -	KEY_OGRAVE = 0x00D2, -	KEY_OACUTE = 0x00D3, -	KEY_OCIRCUMFLEX = 0x00D4, -	KEY_OTILDE = 0x00D5, -	KEY_ODIAERESIS = 0x00D6, -	KEY_MULTIPLY = 0x00D7, -	KEY_OOBLIQUE = 0x00D8, -	KEY_UGRAVE = 0x00D9, -	KEY_UACUTE = 0x00DA, -	KEY_UCIRCUMFLEX = 0x00DB, -	KEY_UDIAERESIS = 0x00DC, -	KEY_YACUTE = 0x00DD, -	KEY_THORN = 0x00DE, -	KEY_SSHARP = 0x00DF, +	COLON = 0x003A, +	SEMICOLON = 0x003B, +	LESS = 0x003C, +	EQUAL = 0x003D, +	GREATER = 0x003E, +	QUESTION = 0x003F, +	AT = 0x0040, +	A = 0x0041, +	B = 0x0042, +	C = 0x0043, +	D = 0x0044, +	E = 0x0045, +	F = 0x0046, +	G = 0x0047, +	H = 0x0048, +	I = 0x0049, +	J = 0x004A, +	K = 0x004B, +	L = 0x004C, +	M = 0x004D, +	N = 0x004E, +	O = 0x004F, +	P = 0x0050, +	Q = 0x0051, +	R = 0x0052, +	S = 0x0053, +	T = 0x0054, +	U = 0x0055, +	V = 0x0056, +	W = 0x0057, +	X = 0x0058, +	Y = 0x0059, +	Z = 0x005A, +	BRACKETLEFT = 0x005B, +	BACKSLASH = 0x005C, +	BRACKETRIGHT = 0x005D, +	ASCIICIRCUM = 0x005E, +	UNDERSCORE = 0x005F, +	QUOTELEFT = 0x0060, +	BRACELEFT = 0x007B, +	BAR = 0x007C, +	BRACERIGHT = 0x007D, +	ASCIITILDE = 0x007E, +	NOBREAKSPACE = 0x00A0, +	EXCLAMDOWN = 0x00A1, +	CENT = 0x00A2, +	STERLING = 0x00A3, +	CURRENCY = 0x00A4, +	YEN = 0x00A5, +	BROKENBAR = 0x00A6, +	SECTION = 0x00A7, +	DIAERESIS = 0x00A8, +	COPYRIGHT = 0x00A9, +	ORDFEMININE = 0x00AA, +	GUILLEMOTLEFT = 0x00AB, +	NOTSIGN = 0x00AC, +	HYPHEN = 0x00AD, +	KEY_REGISTERED = 0x00AE, // "REGISTERED" is a reserved word on Windows. +	MACRON = 0x00AF, +	DEGREE = 0x00B0, +	PLUSMINUS = 0x00B1, +	TWOSUPERIOR = 0x00B2, +	THREESUPERIOR = 0x00B3, +	ACUTE = 0x00B4, +	MU = 0x00B5, +	PARAGRAPH = 0x00B6, +	PERIODCENTERED = 0x00B7, +	CEDILLA = 0x00B8, +	ONESUPERIOR = 0x00B9, +	MASCULINE = 0x00BA, +	GUILLEMOTRIGHT = 0x00BB, +	ONEQUARTER = 0x00BC, +	ONEHALF = 0x00BD, +	THREEQUARTERS = 0x00BE, +	QUESTIONDOWN = 0x00BF, +	AGRAVE = 0x00C0, +	AACUTE = 0x00C1, +	ACIRCUMFLEX = 0x00C2, +	ATILDE = 0x00C3, +	ADIAERESIS = 0x00C4, +	ARING = 0x00C5, +	AE = 0x00C6, +	CCEDILLA = 0x00C7, +	EGRAVE = 0x00C8, +	EACUTE = 0x00C9, +	ECIRCUMFLEX = 0x00CA, +	EDIAERESIS = 0x00CB, +	IGRAVE = 0x00CC, +	IACUTE = 0x00CD, +	ICIRCUMFLEX = 0x00CE, +	IDIAERESIS = 0x00CF, +	ETH = 0x00D0, +	NTILDE = 0x00D1, +	OGRAVE = 0x00D2, +	OACUTE = 0x00D3, +	OCIRCUMFLEX = 0x00D4, +	OTILDE = 0x00D5, +	ODIAERESIS = 0x00D6, +	MULTIPLY = 0x00D7, +	OOBLIQUE = 0x00D8, +	UGRAVE = 0x00D9, +	UACUTE = 0x00DA, +	UCIRCUMFLEX = 0x00DB, +	UDIAERESIS = 0x00DC, +	YACUTE = 0x00DD, +	THORN = 0x00DE, +	SSHARP = 0x00DF, -	KEY_DIVISION = 0x00F7, -	KEY_YDIAERESIS = 0x00FF, +	DIVISION = 0x00F7, +	YDIAERESIS = 0x00FF, +	END_LATIN1 = 0x0100,  }; -enum KeyModifierMask { -	KEY_CODE_MASK = ((1 << 25) - 1), ///< Apply this mask to any keycode to remove modifiers. -	KEY_MODIFIER_MASK = (0xFF << 24), ///< Apply this mask to isolate modifiers. -	KEY_MASK_SHIFT = (1 << 25), -	KEY_MASK_ALT = (1 << 26), -	KEY_MASK_META = (1 << 27), -	KEY_MASK_CTRL = (1 << 28), +enum class KeyModifierMask { +	CODE_MASK = ((1 << 25) - 1), ///< Apply this mask to any keycode to remove modifiers. +	MODIFIER_MASK = (0xFF << 24), ///< Apply this mask to isolate modifiers. +	SHIFT = (1 << 25), +	ALT = (1 << 26), +	META = (1 << 27), +	CTRL = (1 << 28),  #ifdef APPLE_STYLE_KEYS -	KEY_MASK_CMD = KEY_MASK_META, +	CMD = META,  #else -	KEY_MASK_CMD = KEY_MASK_CTRL, +	CMD = CTRL,  #endif - -	KEY_MASK_KPAD = (1 << 29), -	KEY_MASK_GROUP_SWITCH = (1 << 30) -	// bit 31 can't be used because variant uses regular 32 bits int as datatype +	KPAD = (1 << 29), +	GROUP_SWITCH = (1 << 30)  };  // To avoid having unnecessary operators, only define the ones that are needed. @@ -325,10 +318,26 @@ inline Key &operator-=(Key &a, int b) {  	return (Key &)((int &)a -= b);  } +inline Key operator+(Key a, int b) { +	return (Key)((int)a + (int)b); +} +  inline Key operator+(Key a, Key b) { +	return (Key)((int)a + (int)b); +} + +inline Key operator-(Key a, Key b) {  	return (Key)((int)a - (int)b);  } +inline Key operator&(Key a, Key b) { +	return (Key)((int)a & (int)b); +} + +inline Key operator|(Key a, Key b) { +	return (Key)((int)a | (int)b); +} +  inline Key &operator|=(Key &a, Key b) {  	return (Key &)((int &)a |= (int)b);  } @@ -337,6 +346,10 @@ inline Key &operator|=(Key &a, KeyModifierMask b) {  	return (Key &)((int &)a |= (int)b);  } +inline Key &operator&=(Key &a, KeyModifierMask b) { +	return (Key &)((int &)a &= (int)b); +} +  inline Key operator|(Key a, KeyModifierMask b) {  	return (Key)((int)a | (int)b);  } @@ -361,10 +374,10 @@ inline KeyModifierMask operator|(KeyModifierMask a, KeyModifierMask b) {  	return (KeyModifierMask)((int)a | (int)b);  } -String keycode_get_string(uint32_t p_code); -bool keycode_has_unicode(uint32_t p_keycode); -int find_keycode(const String &p_code); -const char *find_keycode_name(int p_keycode); +String keycode_get_string(Key p_code); +bool keycode_has_unicode(Key p_keycode); +Key find_keycode(const String &p_code); +const char *find_keycode_name(Key p_keycode);  int keycode_get_count();  int keycode_get_value_by_index(int p_index);  const char *keycode_get_name_by_index(int p_index); diff --git a/core/os/memory.h b/core/os/memory.h index f67384a17e..ac56a12330 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -42,7 +42,6 @@  #endif  class Memory { -	Memory();  #ifdef DEBUG_ENABLED  	static SafeNumeric<uint64_t> mem_usage;  	static SafeNumeric<uint64_t> max_usage; diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp index ee87346dfc..ee33eef83f 100644 --- a/core/os/midi_driver.cpp +++ b/core/os/midi_driver.cpp @@ -68,46 +68,46 @@ void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_  	}  	switch (event->get_message()) { -		case MIDI_MESSAGE_AFTERTOUCH: +		case MIDIMessage::AFTERTOUCH:  			if (length >= 2 + param_position) {  				event->set_pitch(data[param_position]);  				event->set_pressure(data[param_position + 1]);  			}  			break; -		case MIDI_MESSAGE_CONTROL_CHANGE: +		case MIDIMessage::CONTROL_CHANGE:  			if (length >= 2 + param_position) {  				event->set_controller_number(data[param_position]);  				event->set_controller_value(data[param_position + 1]);  			}  			break; -		case MIDI_MESSAGE_NOTE_ON: -		case MIDI_MESSAGE_NOTE_OFF: +		case MIDIMessage::NOTE_ON: +		case MIDIMessage::NOTE_OFF:  			if (length >= 2 + param_position) {  				event->set_pitch(data[param_position]);  				event->set_velocity(data[param_position + 1]); -				if (event->get_message() == MIDI_MESSAGE_NOTE_ON && event->get_velocity() == 0) { +				if (event->get_message() == MIDIMessage::NOTE_ON && event->get_velocity() == 0) {  					// https://www.midi.org/forum/228-writing-midi-software-send-note-off,-or-zero-velocity-note-on -					event->set_message(MIDI_MESSAGE_NOTE_OFF); +					event->set_message(MIDIMessage::NOTE_OFF);  				}  			}  			break; -		case MIDI_MESSAGE_PITCH_BEND: +		case MIDIMessage::PITCH_BEND:  			if (length >= 2 + param_position) {  				event->set_pitch((data[param_position + 1] << 7) | data[param_position]);  			}  			break; -		case MIDI_MESSAGE_PROGRAM_CHANGE: +		case MIDIMessage::PROGRAM_CHANGE:  			if (length >= 1 + param_position) {  				event->set_instrument(data[param_position]);  			}  			break; -		case MIDI_MESSAGE_CHANNEL_PRESSURE: +		case MIDIMessage::CHANNEL_PRESSURE:  			if (length >= 1 + param_position) {  				event->set_pressure(data[param_position]);  			} diff --git a/core/os/os.cpp b/core/os/os.cpp index 63390919f4..c8b8931919 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -36,7 +36,6 @@  #include "core/io/file_access.h"  #include "core/os/midi_driver.h"  #include "core/version_generated.gen.h" -#include "servers/audio_server.h"  #include <stdarg.h> @@ -76,12 +75,12 @@ void OS::add_logger(Logger *p_logger) {  	}  } -void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type) { +void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, Logger::ErrorType p_type) {  	if (!_stderr_enabled) {  		return;  	} -	_logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_type); +	_logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_editor_notify, p_type);  }  void OS::print(const char *p_format, ...) { @@ -146,6 +145,10 @@ bool OS::is_stdout_verbose() const {  	return _verbose_stdout;  } +bool OS::is_single_window() const { +	return _single_window; +} +  bool OS::is_stdout_debug_enabled() const {  	return _debug_stdout;  } @@ -187,8 +190,8 @@ static void _OS_printres(Object *p_obj) {  }  void OS::print_all_resources(String p_to_file) { -	ERR_FAIL_COND(p_to_file != "" && _OSPRF); -	if (p_to_file != "") { +	ERR_FAIL_COND(!p_to_file.is_empty() && _OSPRF); +	if (!p_to_file.is_empty()) {  		Error err;  		_OSPRF = FileAccess::open(p_to_file, FileAccess::WRITE, &err);  		if (err != OK) { @@ -199,7 +202,7 @@ void OS::print_all_resources(String p_to_file) {  	ObjectDB::debug_objects(_OS_printres); -	if (p_to_file != "") { +	if (!p_to_file.is_empty()) {  		if (_OSPRF) {  			memdelete(_OSPRF);  		} @@ -227,6 +230,12 @@ String OS::get_locale() const {  	return "en";  } +// Non-virtual helper to extract the 2 or 3-letter language code from +// `get_locale()` in a way that's consistent for all platforms. +String OS::get_locale_language() const { +	return get_locale().left(3).replace("_", ""); +} +  // Helper function to ensure that a dir name/path will be valid on the OS  String OS::get_safe_dir_name(const String &p_dir_name, bool p_allow_dir_separator) const {  	Vector<String> invalid_chars = String(": * ? \" < > |").split(" "); @@ -272,6 +281,11 @@ String OS::get_bundle_resource_dir() const {  	return ".";  } +// Path to macOS .app bundle embedded icon +String OS::get_bundle_icon_path() const { +	return String(); +} +  // OS specific path for user://  String OS::get_user_data_dir() const {  	return "."; @@ -357,9 +371,17 @@ void OS::set_has_server_feature_callback(HasServerFeatureCallback p_callback) {  }  bool OS::has_feature(const String &p_feature) { -	if (p_feature == get_name()) { +	// Feature tags are always lowercase for consistency. +	if (p_feature == get_name().to_lower()) {  		return true;  	} + +	// Catch-all `linuxbsd` feature tag that matches on both Linux and BSD. +	// This is the one exposed in the project settings dialog. +	if (p_feature == "linuxbsd" && (get_name() == "Linux" || get_name() == "FreeBSD" || get_name() == "NetBSD" || get_name() == "OpenBSD" || get_name() == "BSD")) { +		return true; +	} +  #ifdef DEBUG_ENABLED  	if (p_feature == "debug") {  		return true; @@ -409,6 +431,24 @@ bool OS::has_feature(const String &p_feature) {  	if (p_feature == "arm") {  		return true;  	} +#elif defined(__riscv) +#if __riscv_xlen == 8 +	if (p_feature == "rv64") { +		return true; +	} +#endif +	if (p_feature == "riscv") { +		return true; +	} +#elif defined(__powerpc__) +#if defined(__powerpc64__) +	if (p_feature == "ppc64") { +		return true; +	} +#endif +	if (p_feature == "ppc") { +		return true; +	}  #endif  	if (_check_internal_feature_support(p_feature)) { diff --git a/core/os/os.h b/core/os/os.h index 55b21266fc..3042696cce 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -52,6 +52,7 @@ class OS {  	int low_processor_usage_mode_sleep_usec = 10000;  	bool _verbose_stdout = false;  	bool _debug_stdout = false; +	bool _single_window = false;  	String _local_clipboard;  	int _exit_code = EXIT_FAILURE; // unexpected exit is marked as failure  	int _orientation; @@ -67,6 +68,11 @@ class OS {  	bool restart_on_exit = false;  	List<String> restart_commandline; +	// for the user interface we keep a record of the current display driver +	// so we can retrieve the rendering drivers available +	int _display_driver_id = -1; +	String _current_rendering_driver_name = ""; +  protected:  	void _set_logger(CompositeLogger *p_logger); @@ -80,6 +86,11 @@ public:  		RENDER_SEPARATE_THREAD  	}; +	enum RenderMainThreadMode { +		RENDER_MAIN_THREAD_ONLY, +		RENDER_ANY_THREAD, +	}; +  protected:  	friend class Main;  	// Needed by tests to setup command-line args. @@ -87,6 +98,7 @@ protected:  	HasServerFeatureCallback has_server_feature_callback = nullptr;  	RenderThreadMode _render_thread_mode = RENDER_THREAD_SAFE; +	RenderMainThreadMode _render_main_thread_mode = RENDER_ANY_THREAD;  	// Functions used by Main to initialize/deinitialize the OS.  	void add_logger(Logger *p_logger); @@ -94,6 +106,9 @@ protected:  	virtual void initialize() = 0;  	virtual void initialize_joypads() = 0; +	void set_current_rendering_driver_name(String p_driver_name) { _current_rendering_driver_name = p_driver_name; } +	void set_display_driver_id(int p_display_driver_id) { _display_driver_id = p_display_driver_id; } +  	virtual void set_main_loop(MainLoop *p_main_loop) = 0;  	virtual void delete_main_loop() = 0; @@ -109,7 +124,10 @@ public:  	static OS *get_singleton(); -	void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type = Logger::ERR_ERROR); +	String get_current_rendering_driver_name() const { return _current_rendering_driver_name; } +	int get_display_driver_id() const { return _display_driver_id; } + +	void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, Logger::ErrorType p_type = Logger::ERR_ERROR);  	void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;  	void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; @@ -133,6 +151,7 @@ public:  	virtual String get_executable_path() const;  	virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) = 0;  	virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) = 0; +	virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) { return create_process(get_executable_path(), p_arguments, r_child_id); };  	virtual Error kill(const ProcessID &p_pid) = 0;  	virtual int get_process_id() const;  	virtual void vibrate_handheld(int p_duration_ms = 500); @@ -203,8 +222,8 @@ public:  		String name;  	}; -	virtual Date get_date(bool local = false) const = 0; -	virtual Time get_time(bool local = false) const = 0; +	virtual Date get_date(bool p_utc = false) const = 0; +	virtual Time get_time(bool p_utc = false) const = 0;  	virtual TimeZoneInfo get_time_zone_info() const = 0;  	virtual double get_unix_time() const; @@ -224,6 +243,8 @@ public:  	void set_stdout_enabled(bool p_enabled);  	void set_stderr_enabled(bool p_enabled); +	virtual bool is_single_window() const; +  	virtual void disable_crash_handler() {}  	virtual bool is_disable_crash_handler() const { return false; }  	virtual void initialize_debugging() {} @@ -238,8 +259,11 @@ public:  	virtual uint64_t get_free_static_memory() const;  	RenderThreadMode get_render_thread_mode() const { return _render_thread_mode; } +	RenderMainThreadMode get_render_main_thread_mode() const { return _render_main_thread_mode; } +	void set_render_main_thread_mode(RenderMainThreadMode p_thread_mode) { _render_main_thread_mode = p_thread_mode; }  	virtual String get_locale() const; +	String get_locale_language() const;  	String get_safe_dir_name(const String &p_dir_name, bool p_allow_dir_separator = false) const;  	virtual String get_godot_dir_name() const; @@ -248,6 +272,7 @@ public:  	virtual String get_config_path() const;  	virtual String get_cache_path() const;  	virtual String get_bundle_resource_dir() const; +	virtual String get_bundle_icon_path() const;  	virtual String get_user_data_dir() const;  	virtual String get_resource_dir() const; @@ -273,6 +298,7 @@ public:  	virtual void set_exit_code(int p_code);  	virtual int get_processor_count() const; +	virtual int get_default_thread_pool_size() const { return get_processor_count(); }  	virtual String get_unique_id() const; diff --git a/core/os/thread.cpp b/core/os/thread.cpp index 73e31bdb3d..27aefc98de 100644 --- a/core/os/thread.cpp +++ b/core/os/thread.cpp @@ -28,6 +28,8 @@  /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */  /*************************************************************************/ +#ifndef PLATFORM_THREAD_OVERRIDE // See details in thread.h +  #include "thread.h"  #include "core/object/script_language.h" @@ -126,3 +128,4 @@ Thread::~Thread() {  }  #endif +#endif // PLATFORM_THREAD_OVERRIDE diff --git a/core/os/thread.h b/core/os/thread.h index 17ac82c650..59cb58ac57 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -28,6 +28,12 @@  /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */  /*************************************************************************/ +// Define PLATFORM_THREAD_OVERRIDE in your platform's `platform_config.h` +// to use a custom Thread implementation defined in `platform/[your_platform]/platform_thread.h` +// Overriding the platform implementation is required in some proprietary platforms +#ifdef PLATFORM_THREAD_OVERRIDE +#include "platform_thread.h" +#else  #ifndef THREAD_H  #define THREAD_H @@ -116,3 +122,4 @@ public:  };  #endif // THREAD_H +#endif // PLATFORM_THREAD_OVERRIDE diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 6c7d9cbd89..5479041b29 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -48,9 +48,6 @@  #include "core/io/image_loader.h"  #include "core/io/json.h"  #include "core/io/marshalls.h" -#include "core/io/multiplayer_api.h" -#include "core/io/multiplayer_peer.h" -#include "core/io/multiplayer_replicator.h"  #include "core/io/packed_data_container.h"  #include "core/io/packet_peer.h"  #include "core/io/packet_peer_dtls.h" @@ -70,6 +67,9 @@  #include "core/math/geometry_3d.h"  #include "core/math/random_number_generator.h"  #include "core/math/triangle_mesh.h" +#include "core/multiplayer/multiplayer_api.h" +#include "core/multiplayer/multiplayer_peer.h" +#include "core/multiplayer/multiplayer_replicator.h"  #include "core/object/class_db.h"  #include "core/object/undo_redo.h"  #include "core/os/main_loop.h" @@ -169,11 +169,13 @@ void register_core_types() {  	GDREGISTER_VIRTUAL_CLASS(IP);  	GDREGISTER_VIRTUAL_CLASS(StreamPeer); +	GDREGISTER_CLASS(StreamPeerExtension);  	GDREGISTER_CLASS(StreamPeerBuffer);  	GDREGISTER_CLASS(StreamPeerTCP);  	GDREGISTER_CLASS(TCPServer);  	GDREGISTER_VIRTUAL_CLASS(PacketPeer); +	GDREGISTER_CLASS(PacketPeerExtension);  	GDREGISTER_CLASS(PacketPeerStream);  	GDREGISTER_CLASS(PacketPeerUDP);  	GDREGISTER_CLASS(UDPServer); @@ -197,6 +199,7 @@ void register_core_types() {  	ResourceLoader::add_resource_format_loader(resource_format_loader_crypto);  	GDREGISTER_VIRTUAL_CLASS(MultiplayerPeer); +	GDREGISTER_CLASS(MultiplayerPeerExtension);  	GDREGISTER_VIRTUAL_CLASS(MultiplayerReplicator);  	GDREGISTER_CLASS(MultiplayerAPI);  	GDREGISTER_CLASS(MainLoop); diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp index 5fae13779e..bb9a44ccaf 100644 --- a/core/string/node_path.cpp +++ b/core/string/node_path.cpp @@ -293,12 +293,12 @@ void NodePath::simplify() {  			break;  		}  		if (data->path[i].operator String() == ".") { -			data->path.remove(i); +			data->path.remove_at(i);  			i--;  		} else if (i > 0 && data->path[i].operator String() == ".." && data->path[i - 1].operator String() != "." && data->path[i - 1].operator String() != "..") {  			//remove both -			data->path.remove(i - 1); -			data->path.remove(i - 1); +			data->path.remove_at(i - 1); +			data->path.remove_at(i - 1);  			i -= 2;  			if (data->path.size() == 0) {  				data->path.push_back("."); @@ -368,7 +368,7 @@ NodePath::NodePath(const String &p_path) {  		for (int i = from; i <= path.length(); i++) {  			if (path[i] == ':' || path[i] == 0) {  				String str = path.substr(from, i - from); -				if (str == "") { +				if (str.is_empty()) {  					if (path[i] == 0) {  						continue; // Allow end-of-path :  					} diff --git a/core/string/optimized_translation.cpp b/core/string/optimized_translation.cpp index 5863bd1c46..f8be564740 100644 --- a/core/string/optimized_translation.cpp +++ b/core/string/optimized_translation.cpp @@ -64,7 +64,6 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) {  	int idx = 0;  	int total_compression_size = 0; -	int total_string_size = 0;  	for (const StringName &E : keys) {  		//hash string @@ -102,7 +101,6 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) {  		compressed.write[idx] = ps;  		total_compression_size += ps.compressed.size(); -		total_string_size += src_s.size();  		idx++;  	} @@ -147,26 +145,23 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) {  	uint32_t *btw = (uint32_t *)&btwb[0];  	int btindex = 0; -	int collisions = 0;  	for (int i = 0; i < size; i++) {  		const Map<uint32_t, int> &t = table[i];  		if (t.size() == 0) {  			htw[i] = 0xFFFFFFFF; //nothing  			continue; -		} else if (t.size() > 1) { -			collisions += t.size() - 1;  		}  		htw[i] = btindex;  		btw[btindex++] = t.size();  		btw[btindex++] = hfunc_table[i]; -		for (Map<uint32_t, int>::Element *E = t.front(); E; E = E->next()) { -			btw[btindex++] = E->key(); -			btw[btindex++] = compressed[E->get()].offset; -			btw[btindex++] = compressed[E->get()].compressed.size(); -			btw[btindex++] = compressed[E->get()].orig_len; +		for (const KeyValue<uint32_t, int> &E : t) { +			btw[btindex++] = E.key; +			btw[btindex++] = compressed[E.value].offset; +			btw[btindex++] = compressed[E.value].compressed.size(); +			btw[btindex++] = compressed[E.value].orig_len;  		}  	} diff --git a/core/string/print_string.cpp b/core/string/print_string.cpp index 345371d733..adc218f597 100644 --- a/core/string/print_string.cpp +++ b/core/string/print_string.cpp @@ -69,7 +69,7 @@ void remove_print_handler(PrintHandlerList *p_handler) {  	ERR_FAIL_COND(l == nullptr);  } -void print_line(String p_string) { +void __print_line(String p_string) {  	if (!_print_line_enabled) {  		return;  	} @@ -108,3 +108,7 @@ void print_verbose(String p_string) {  		print_line(p_string);  	}  } + +String stringify_variants(Variant p_var) { +	return p_var.operator String(); +} diff --git a/core/string/print_string.h b/core/string/print_string.h index 1a9ff1efd6..3cd170b68e 100644 --- a/core/string/print_string.h +++ b/core/string/print_string.h @@ -31,7 +31,7 @@  #ifndef PRINT_STRING_H  #define PRINT_STRING_H -#include "core/string/ustring.h" +#include "core/variant/variant.h"  extern void (*_print_func)(String); @@ -46,13 +46,21 @@ struct PrintHandlerList {  	PrintHandlerList() {}  }; +String stringify_variants(Variant p_var); + +template <typename... Args> +String stringify_variants(Variant p_var, Args... p_args) { +	return p_var.operator String() + " " + stringify_variants(p_args...); +} +  void add_print_handler(PrintHandlerList *p_handler);  void remove_print_handler(PrintHandlerList *p_handler);  extern bool _print_line_enabled;  extern bool _print_error_enabled; -extern void print_line(String p_string); +extern void __print_line(String p_string);  extern void print_error(String p_string);  extern void print_verbose(String p_string); +#define print_line(...) __print_line(stringify_variants(__VA_ARGS__))  #endif // PRINT_STRING_H diff --git a/core/string/string_builder.cpp b/core/string/string_builder.cpp index 834c87c845..45cc2f3280 100644 --- a/core/string/string_builder.cpp +++ b/core/string/string_builder.cpp @@ -33,7 +33,7 @@  #include <string.h>  StringBuilder &StringBuilder::append(const String &p_string) { -	if (p_string == String()) { +	if (p_string.is_empty()) {  		return *this;  	} diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index 9024f60dae..0e3482e873 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -310,7 +310,7 @@ StringName::StringName(const String &p_name, bool p_static) {  	ERR_FAIL_COND(!configured); -	if (p_name == String()) { +	if (p_name.is_empty()) {  		return;  	} @@ -434,7 +434,7 @@ StringName StringName::search(const char32_t *p_name) {  }  StringName StringName::search(const String &p_name) { -	ERR_FAIL_COND_V(p_name == "", StringName()); +	ERR_FAIL_COND_V(p_name.is_empty(), StringName());  	MutexLock lock(mutex); diff --git a/core/string/translation.cpp b/core/string/translation.cpp index cb7d924556..865937e9ce 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -35,7 +35,6 @@  #include "core/os/os.h"  #ifdef TOOLS_ENABLED -#include "editor/editor_settings.h"  #include "main/main.h"  #endif @@ -810,9 +809,12 @@ static const char *locale_names[] = {  // - https://msdn.microsoft.com/en-us/library/windows/desktop/ms693062(v=vs.85).aspx  static const char *locale_renames[][2] = { -	{ "in", "id" }, //  Indonesian -	{ "iw", "he" }, //  Hebrew -	{ "no", "nb" }, //  Norwegian Bokmål +	{ "in", "id" }, // Indonesian +	{ "iw", "he" }, // Hebrew +	{ "no", "nb" }, // Norwegian Bokmål +	{ "C", "en" }, // "C" is the simple/default/untranslated Computer locale. +	// ASCII-only, English, no currency symbols. Godot treats this as "en". +	// See https://unix.stackexchange.com/a/87763/164141 "The C locale is"...  	{ nullptr, nullptr }  }; @@ -820,8 +822,8 @@ static const char *locale_renames[][2] = {  Dictionary Translation::_get_messages() const {  	Dictionary d; -	for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) { -		d[E->key()] = E->value(); +	for (const KeyValue<StringName, StringName> &E : translation_map) { +		d[E.key] = E.value;  	}  	return d;  } @@ -830,8 +832,8 @@ Vector<String> Translation::_get_message_list() const {  	Vector<String> msgs;  	msgs.resize(translation_map.size());  	int idx = 0; -	for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) { -		msgs.set(idx, E->key()); +	for (const KeyValue<StringName, StringName> &E : translation_map) { +		msgs.set(idx, E.key);  		idx += 1;  	} @@ -875,6 +877,11 @@ void Translation::add_plural_message(const StringName &p_src_text, const Vector<  }  StringName Translation::get_message(const StringName &p_src_text, const StringName &p_context) const { +	StringName ret; +	if (GDVIRTUAL_CALL(_get_message, p_src_text, p_context, ret)) { +		return ret; +	} +  	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");  	} @@ -888,6 +895,11 @@ StringName Translation::get_message(const StringName &p_src_text, const StringNa  }  StringName Translation::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const { +	StringName ret; +	if (GDVIRTUAL_CALL(_get_plural_message, p_src_text, p_plural_text, p_n, p_context, ret)) { +		return ret; +	} +  	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);  } @@ -901,8 +913,8 @@ void Translation::erase_message(const StringName &p_src_text, const StringName &  }  void Translation::get_message_list(List<StringName> *r_messages) const { -	for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) { -		r_messages->push_back(E->key()); +	for (const KeyValue<StringName, StringName> &E : translation_map) { +		r_messages->push_back(E.key);  	}  } @@ -923,7 +935,10 @@ void Translation::_bind_methods() {  	ClassDB::bind_method(D_METHOD("_set_messages"), &Translation::_set_messages);  	ClassDB::bind_method(D_METHOD("_get_messages"), &Translation::_get_messages); -	ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages"); +	GDVIRTUAL_BIND(_get_plural_message, "src_message", "src_plural_message", "n", "context"); +	GDVIRTUAL_BIND(_get_message, "src_message", "context"); + +	ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages");  	ADD_PROPERTY(PropertyInfo(Variant::STRING, "locale"), "set_locale", "get_locale");  } @@ -1077,9 +1092,12 @@ Array TranslationServer::get_loaded_locales() const {  		ERR_FAIL_COND_V(t.is_null(), Array());  		String l = t->get_locale(); -		locales.push_back(l); +		if (!locales.has(l)) { +			locales.push_back(l); +		}  	} +	locales.sort();  	return locales;  } @@ -1272,7 +1290,7 @@ bool TranslationServer::_load_translations(const String &p_from) {  void TranslationServer::setup() {  	String test = GLOBAL_DEF("internationalization/locale/test", "");  	test = test.strip_edges(); -	if (test != "") { +	if (!test.is_empty()) {  		set_locale(test);  	} else {  		set_locale(OS::get_singleton()->get_locale()); @@ -1548,8 +1566,8 @@ const char32_t *TranslationServer::get_accented_version(char32_t p_character) co  bool TranslationServer::is_placeholder(String &p_message, int p_index) const {  	return p_message[p_index] == '%' && p_index < p_message.size() - 1 && -		   (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' || -				   p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f'); +			(p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' || +					p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f');  }  void TranslationServer::_bind_methods() { diff --git a/core/string/translation.h b/core/string/translation.h index 4f179ac0fe..6aec0bb8ea 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -32,6 +32,8 @@  #define TRANSLATION_H  #include "core/io/resource.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h"  class Translation : public Resource {  	GDCLASS(Translation, Resource); @@ -48,6 +50,9 @@ class Translation : public Resource {  protected:  	static void _bind_methods(); +	GDVIRTUAL2RC(StringName, _get_message, StringName, StringName); +	GDVIRTUAL4RC(StringName, _get_plural_message, StringName, StringName, int, StringName); +  public:  	void set_locale(const String &p_locale);  	_FORCE_INLINE_ String get_locale() const { return locale; } diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index a30d6b9102..ac8e2ece12 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -38,6 +38,7 @@  #include "core/string/translation.h"  #include "core/string/ucaps.h"  #include "core/variant/variant.h" +#include "core/version_generated.gen.h"  #include <stdio.h>  #include <stdlib.h> @@ -129,9 +130,8 @@ Char16String &Char16String::operator+=(char16_t p_char) {  	return *this;  } -Char16String &Char16String::operator=(const char16_t *p_cstr) { +void Char16String::operator=(const char16_t *p_cstr) {  	copy_from(p_cstr); -	return *this;  }  const char16_t *Char16String::get_data() const { @@ -185,9 +185,8 @@ CharString &CharString::operator+=(char p_char) {  	return *this;  } -CharString &CharString::operator=(const char *p_cstr) { +void CharString::operator=(const char *p_cstr) {  	copy_from(p_cstr); -	return *this;  }  const char *CharString::get_data() const { @@ -952,10 +951,6 @@ const char32_t *String::get_data() const {  	return size() ? &operator[](0) : &zero;  } -void String::erase(int p_pos, int p_chars) { -	*this = left(MAX(p_pos, 0)) + substr(p_pos + p_chars, length() - ((p_pos + p_chars))); -} -  String String::capitalize() const {  	String aux = this->camelcase_to_underscore(true).replace("_", " ").strip_edges();  	String cap; @@ -1551,19 +1546,21 @@ String String::num_real(double p_num, bool p_trailing) {  	bool neg = p_num < 0;  	p_num = ABS(p_num); -	int intn = (int)p_num; +	int64_t intn = (int64_t)p_num;  	// Decimal part.  	if (intn != p_num) { -		double dec = p_num - (double)(intn); +		double dec = p_num - (double)intn;  		int digit = 0; -#if REAL_T_IS_DOUBLE +#ifdef REAL_T_IS_DOUBLE  		int decimals = 14; +		double tolerance = 1e-14;  #else  		int decimals = 6; +		double tolerance = 1e-6;  #endif  		// We want to align the digits to the above sane default, so we only  		// need to subtract log10 for numbers with a positive power of ten. @@ -1575,16 +1572,21 @@ String String::num_real(double p_num, bool p_trailing) {  			decimals = MAX_DECIMALS;  		} -		int dec_int = 0; -		int dec_max = 0; +		// In case the value ends up ending in "99999", we want to add a +		// tiny bit to the value we're checking when deciding when to stop, +		// so we multiply by slightly above 1 (1 + 1e-7 or 1e-15). +		double check_multiplier = 1 + tolerance / 10; + +		int64_t dec_int = 0; +		int64_t dec_max = 0;  		while (true) {  			dec *= 10.0; -			dec_int = dec_int * 10 + (int)dec % 10; +			dec_int = dec_int * 10 + (int64_t)dec % 10;  			dec_max = dec_max * 10 + 9;  			digit++; -			if ((dec - (double)((int)dec)) < 1e-6) { +			if ((dec - (double)(int64_t)(dec * check_multiplier)) < tolerance) {  				break;  			} @@ -1594,7 +1596,7 @@ String String::num_real(double p_num, bool p_trailing) {  		}  		dec *= 10; -		int last = (int)dec % 10; +		int last = (int64_t)dec % 10;  		if (last > 5) {  			if (dec_int == dec_max) { @@ -1653,13 +1655,13 @@ String String::num_scientific(double p_num) {  #if defined(__GNUC__) || defined(_MSC_VER) -#if (defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER < 1900)) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT) -	// MinGW and old MSC require _set_output_format() to conform to C99 output for printf +#if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT) +	// MinGW requires _set_output_format() to conform to C99 output for printf  	unsigned int old_exponent_format = _set_output_format(_TWO_DIGIT_EXPONENT);  #endif  	snprintf(buf, 256, "%lg", p_num); -#if (defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER < 1900)) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT) +#if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)  	_set_output_format(old_exponent_format);  #endif @@ -2130,7 +2132,7 @@ int64_t String::hex_to_int() const {  		s++;  	} -	if (len > 2 && s[0] == '0' && s[1] == 'x') { +	if (len > 2 && s[0] == '0' && lower_case(s[1]) == 'x') {  		s += 2;  	} @@ -2144,7 +2146,7 @@ int64_t String::hex_to_int() const {  		} else if (c >= 'a' && c <= 'f') {  			n = (c - 'a') + 10;  		} else { -			return 0; +			ERR_FAIL_COND_V_MSG(true, 0, "Invalid hexadecimal notation character \"" + chr(*s) + "\" in string \"" + *this + "\".");  		}  		// 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'); @@ -2171,7 +2173,7 @@ int64_t String::bin_to_int() const {  		s++;  	} -	if (len > 2 && s[0] == '0' && s[1] == 'b') { +	if (len > 2 && s[0] == '0' && lower_case(s[1]) == 'b') {  		s += 2;  	} @@ -2310,28 +2312,33 @@ bool String::is_numeric() const {  }  template <class C> -static double built_in_strtod(const C *string, /* A decimal ASCII floating-point number, -				 * optionally preceded by white space. Must -				 * have form "-I.FE-X", where I is the integer -				 * part of the mantissa, F is the fractional -				 * part of the mantissa, and X is the -				 * exponent. Either of the signs may be "+", -				 * "-", or omitted. Either I or F may be -				 * omitted, or both. The decimal point isn't -				 * necessary unless F is present. The "E" may -				 * actually be an "e". E and X may both be -				 * omitted (but not just one). */ -		C **endPtr = nullptr) /* If non-nullptr, store terminating Cacter's -				 * address here. */ -{ -	static const int maxExponent = 511; /* Largest possible base 10 exponent.  Any -					 * exponent larger than this will already -					 * produce underflow or overflow, so there's -					 * no need to worry about additional digits. -					 */ -	static const double powersOf10[] = { /* Table giving binary powers of 10.  Entry */ -		10., /* is 10^2^i.  Used to convert decimal */ -		100., /* exponents into floating-point numbers. */ +static double built_in_strtod( +		/* A decimal ASCII floating-point number, +		 * optionally preceded by white space. Must +		 * have form "-I.FE-X", where I is the integer +		 * part of the mantissa, F is the fractional +		 * part of the mantissa, and X is the +		 * exponent. Either of the signs may be "+", +		 * "-", or omitted. Either I or F may be +		 * omitted, or both. The decimal point isn't +		 * necessary unless F is present. The "E" may +		 * actually be an "e". E and X may both be +		 * omitted (but not just one). */ +		const C *string, +		/* If non-nullptr, store terminating Cacter's +		 * address here. */ +		C **endPtr = nullptr) { +	/* Largest possible base 10 exponent. Any +	 * exponent larger than this will already +	 * produce underflow or overflow, so there's +	 * no need to worry about additional digits. */ +	static const int maxExponent = 511; +	/* Table giving binary powers of 10. Entry +	 * is 10^2^i. Used to convert decimal +	 * exponents into floating-point numbers. */ +	static const double powersOf10[] = { +		10., +		100.,  		1.0e4,  		1.0e8,  		1.0e16, @@ -2346,25 +2353,28 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point  	const double *d;  	const C *p;  	int c; -	int exp = 0; /* Exponent read from "EX" field. */ -	int fracExp = 0; /* Exponent that derives from the fractional -				 * part. Under normal circumstances, it is -				 * the negative of the number of digits in F. -				 * However, if I is very long, the last digits -				 * of I get dropped (otherwise a long I with a -				 * large negative exponent could cause an -				 * unnecessary overflow on I alone). In this -				 * case, fracExp is incremented one for each -				 * dropped digit. */ -	int mantSize; /* Number of digits in mantissa. */ -	int decPt; /* Number of mantissa digits BEFORE decimal -				 * point. */ -	const C *pExp; /* Temporarily holds location of exponent in -				 * string. */ +	/* Exponent read from "EX" field. */ +	int exp = 0; +	/* Exponent that derives from the fractional +	 * part. Under normal circumstances, it is +	 * the negative of the number of digits in F. +	 * However, if I is very long, the last digits +	 * of I get dropped (otherwise a long I with a +	 * large negative exponent could cause an +	 * unnecessary overflow on I alone). In this +	 * case, fracExp is incremented one for each +	 * dropped digit. */ +	int fracExp = 0; +	/* Number of digits in mantissa. */ +	int mantSize; +	/* Number of mantissa digits BEFORE decimal point. */ +	int decPt; +	/* Temporarily holds location of exponent in string. */ +	const C *pExp;  	/* -     * Strip off leading blanks and check for a sign. -     */ +	 * Strip off leading blanks and check for a sign. +	 */  	p = string;  	while (*p == ' ' || *p == '\t' || *p == '\n') { @@ -2381,9 +2391,9 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point  	}  	/* -     * Count the number of digits in the mantissa (including the decimal -     * point), and also locate the decimal point. -     */ +	 * Count the number of digits in the mantissa (including the decimal +	 * point), and also locate the decimal point. +	 */  	decPt = -1;  	for (mantSize = 0;; mantSize += 1) { @@ -2398,11 +2408,11 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point  	}  	/* -     * Now suck up the digits in the mantissa. Use two integers to collect 9 -     * digits each (this is faster than using floating-point). If the mantissa -     * has more than 18 digits, ignore the extras, since they can't affect the -     * value anyway. -     */ +	 * Now suck up the digits in the mantissa. Use two integers to collect 9 +	 * digits each (this is faster than using floating-point). If the mantissa +	 * has more than 18 digits, ignore the extras, since they can't affect the +	 * value anyway. +	 */  	pExp = p;  	p -= mantSize; @@ -2448,8 +2458,8 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point  	}  	/* -     * Skim off the exponent. -     */ +	 * Skim off the exponent. +	 */  	p = pExp;  	if ((*p == 'E') || (*p == 'e')) { @@ -2479,10 +2489,10 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point  	}  	/* -     * Generate a floating-point number that represents the exponent. Do this -     * by processing the exponent one bit at a time to combine many powers of -     * 2 of 10. Then combine the exponent with the fraction. -     */ +	 * Generate a floating-point number that represents the exponent. Do this +	 * by processing the exponent one bit at a time to combine many powers of +	 * 2 of 10. Then combine the exponent with the fraction. +	 */  	if (exp < 0) {  		expSign = true; @@ -3555,7 +3565,7 @@ String String::strip_edges(bool left, bool right) const {  	}  	if (right) { -		for (int i = (int)(len - 1); i >= 0; i--) { +		for (int i = len - 1; i >= 0; i--) {  			if (operator[](i) <= 32) {  				end--;  			} else { @@ -3658,15 +3668,15 @@ String String::simplify_path() const {  	for (int i = 0; i < dirs.size(); i++) {  		String d = dirs[i];  		if (d == ".") { -			dirs.remove(i); +			dirs.remove_at(i);  			i--;  		} else if (d == "..") {  			if (i == 0) { -				dirs.remove(i); +				dirs.remove_at(i);  				i--;  			} else { -				dirs.remove(i); -				dirs.remove(i - 1); +				dirs.remove_at(i); +				dirs.remove_at(i - 1);  				i -= 2;  			}  		} @@ -4273,7 +4283,7 @@ bool String::is_valid_filename() const {  		return false;  	} -	if (stripped == String()) { +	if (stripped.is_empty()) {  		return false;  	} @@ -4324,28 +4334,44 @@ bool String::is_resource_file() const {  	return begins_with("res://") && find("::") == -1;  } -bool String::is_rel_path() const { +bool String::is_relative_path() const {  	return !is_absolute_path();  }  String String::get_base_dir() const { -	int basepos = find(":/"); -	if (basepos == -1) { -		basepos = find(":\\"); +	int end = 0; + +	// url scheme style base +	int basepos = find("://"); +	if (basepos != -1) { +		end = basepos + 3;  	} + +	// windows top level directory base +	if (end == 0) { +		basepos = find(":/"); +		if (basepos == -1) { +			basepos = find(":\\"); +		} +		if (basepos != -1) { +			end = basepos + 2; +		} +	} + +	// unix root directory base +	if (end == 0) { +		if (begins_with("/")) { +			end = 1; +		} +	} +  	String rs;  	String base; -	if (basepos != -1) { -		int end = basepos + 3; +	if (end != 0) {  		rs = substr(end, length());  		base = substr(0, end);  	} else { -		if (begins_with("/")) { -			rs = substr(1, length()); -			base = "/"; -		} else { -			rs = *this; -		} +		rs = *this;  	}  	int sep = MAX(rs.rfind("/"), rs.rfind("\\")); @@ -4389,7 +4415,7 @@ String String::property_name_encode() const {  	// as well as '"', '=' or ' ' (32)  	const char32_t *cstr = get_data();  	for (int i = 0; cstr[i]; i++) { -		if (cstr[i] == '=' || cstr[i] == '"' || cstr[i] < 33 || cstr[i] > 126) { +		if (cstr[i] == '=' || cstr[i] == '"' || cstr[i] == ';' || cstr[i] == '[' || cstr[i] == ']' || cstr[i] < 33 || cstr[i] > 126) {  			return "\"" + c_escape_multiline() + "\"";  		}  	} @@ -4841,15 +4867,20 @@ String TTRN(const String &p_text, const String &p_text_plural, int p_n, const St  	return p_text_plural;  } +/* DTR and DTRN are used for the documentation, handling descriptions extracted + * from the XML. + * They also replace `$DOCS_URL` with the actual URL to the documentation's branch, + * to allow dehardcoding it in the XML and doing proper substitutions everywhere. + */  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, p_context); +		return String(TranslationServer::get_singleton()->doc_translate(text, p_context)).replace("$DOCS_URL", VERSION_DOCS_URL);  	} -	return text; +	return text.replace("$DOCS_URL", VERSION_DOCS_URL);  }  String DTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context) { @@ -4857,21 +4888,21 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St  	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 String(TranslationServer::get_singleton()->doc_translate_plural(text, text_plural, p_n, p_context)).replace("$DOCS_URL", VERSION_DOCS_URL);  	}  	// Return message based on English plural rule if translation is not possible.  	if (p_n == 1) { -		return text; +		return text.replace("$DOCS_URL", VERSION_DOCS_URL);  	} -	return text_plural; +	return text_plural.replace("$DOCS_URL", VERSION_DOCS_URL);  }  #endif  String RTR(const String &p_text, const String &p_context) {  	if (TranslationServer::get_singleton()) {  		String rtr = TranslationServer::get_singleton()->tool_translate(p_text, p_context); -		if (rtr == String() || rtr == p_text) { +		if (rtr.is_empty() || rtr == p_text) {  			return TranslationServer::get_singleton()->translate(p_text, p_context);  		} else {  			return rtr; @@ -4884,7 +4915,7 @@ 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) {  	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) { +		if (rtr.is_empty() || 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; diff --git a/core/string/ustring.h b/core/string/ustring.h index ffb354d6e1..396c996050 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -51,11 +51,15 @@ class CharProxy {  	CowData<T> &_cowdata;  	static const T _null = 0; -	_FORCE_INLINE_ CharProxy(const int &p_index, CowData<T> &cowdata) : +	_FORCE_INLINE_ CharProxy(const int &p_index, CowData<T> &p_cowdata) :  			_index(p_index), -			_cowdata(cowdata) {} +			_cowdata(p_cowdata) {}  public: +	_FORCE_INLINE_ CharProxy(const CharProxy<T> &p_other) : +			_index(p_other._index), +			_cowdata(p_other._cowdata) {} +  	_FORCE_INLINE_ operator T() const {  		if (unlikely(_index == _cowdata.size())) {  			return _null; @@ -68,12 +72,12 @@ public:  		return _cowdata.ptr() + _index;  	} -	_FORCE_INLINE_ void operator=(const T &other) const { -		_cowdata.set(_index, other); +	_FORCE_INLINE_ void operator=(const T &p_other) const { +		_cowdata.set(_index, p_other);  	} -	_FORCE_INLINE_ void operator=(const CharProxy<T> &other) const { -		_cowdata.set(_index, other.operator T()); +	_FORCE_INLINE_ void operator=(const CharProxy<T> &p_other) const { +		_cowdata.set(_index, p_other.operator T());  	}  }; @@ -104,13 +108,10 @@ public:  	_FORCE_INLINE_ Char16String() {}  	_FORCE_INLINE_ Char16String(const Char16String &p_str) { _cowdata._ref(p_str._cowdata); } -	_FORCE_INLINE_ Char16String &operator=(const Char16String &p_str) { -		_cowdata._ref(p_str._cowdata); -		return *this; -	} +	_FORCE_INLINE_ void operator=(const Char16String &p_str) { _cowdata._ref(p_str._cowdata); }  	_FORCE_INLINE_ Char16String(const char16_t *p_cstr) { copy_from(p_cstr); } -	Char16String &operator=(const char16_t *p_cstr); +	void operator=(const char16_t *p_cstr);  	bool operator<(const Char16String &p_right) const;  	Char16String &operator+=(char16_t p_char);  	int length() const { return size() ? size() - 1 : 0; } @@ -148,13 +149,10 @@ public:  	_FORCE_INLINE_ CharString() {}  	_FORCE_INLINE_ CharString(const CharString &p_str) { _cowdata._ref(p_str._cowdata); } -	_FORCE_INLINE_ CharString &operator=(const CharString &p_str) { -		_cowdata._ref(p_str._cowdata); -		return *this; -	} +	_FORCE_INLINE_ void operator=(const CharString &p_str) { _cowdata._ref(p_str._cowdata); }  	_FORCE_INLINE_ CharString(const char *p_cstr) { copy_from(p_cstr); } -	CharString &operator=(const char *p_cstr); +	void operator=(const char *p_cstr);  	bool operator<(const CharString &p_right) const;  	CharString &operator+=(char p_char);  	int length() const { return size() ? size() - 1 : 0; } @@ -205,7 +203,7 @@ public:  	_FORCE_INLINE_ char32_t *ptrw() { return _cowdata.ptrw(); }  	_FORCE_INLINE_ const char32_t *ptr() const { return _cowdata.ptr(); } -	void remove(int p_index) { _cowdata.remove(p_index); } +	void remove_at(int p_index) { _cowdata.remove_at(p_index); }  	_FORCE_INLINE_ void clear() { resize(0); } @@ -398,7 +396,7 @@ public:  	// path functions  	bool is_absolute_path() const; -	bool is_rel_path() const; +	bool is_relative_path() const;  	bool is_resource_file() const;  	String path_to(const String &p_path) const;  	String path_to_file(const String &p_path) const; @@ -438,11 +436,7 @@ public:  	_FORCE_INLINE_ String() {}  	_FORCE_INLINE_ String(const String &p_str) { _cowdata._ref(p_str._cowdata); } - -	String &operator=(const String &p_str) { -		_cowdata._ref(p_str._cowdata); -		return *this; -	} +	_FORCE_INLINE_ void operator=(const String &p_str) { _cowdata._ref(p_str._cowdata); }  	Vector<uint8_t> to_ascii_buffer() const;  	Vector<uint8_t> to_utf8_buffer() const; @@ -523,10 +517,10 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St  #define TTRGET(m_value) TTR(m_value)  #else -#define TTR(m_value) (String()) -#define TTRN(m_value) (String()) -#define DTR(m_value) (String()) -#define DTRN(m_value) (String()) +#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 diff --git a/core/templates/bin_sorted_array.h b/core/templates/bin_sorted_array.h index be9d0b5475..8db3e7aeb8 100644 --- a/core/templates/bin_sorted_array.h +++ b/core/templates/bin_sorted_array.h @@ -112,7 +112,7 @@ public:  		return current_idx;  	} -	void remove(uint64_t p_idx) { +	void remove_at(uint64_t p_idx) {  		ERR_FAIL_COND(p_idx >= array.size());  		uint64_t new_idx = move(p_idx, 0);  		uint64_t swap_idx = array.size() - 1; diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h index ba9babe0af..e79ca037db 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -49,6 +49,12 @@ class VMap;  SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t)  #endif +// Silence a false positive warning (see GH-52119). +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wplacement-new" +#endif +  template <class T>  class CowData {  	template <class TV> @@ -161,7 +167,7 @@ public:  	Error resize(int p_size); -	_FORCE_INLINE_ void remove(int p_index) { +	_FORCE_INLINE_ void remove_at(int p_index) {  		ERR_FAIL_INDEX(p_index, size());  		T *p = ptrw();  		int len = size(); @@ -380,4 +386,8 @@ CowData<T>::~CowData() {  	_unref(_ptr);  } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif +  #endif // COWDATA_H diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h index 1257b54449..45e0cc2427 100644 --- a/core/templates/hash_map.h +++ b/core/templates/hash_map.h @@ -53,7 +53,7 @@   * @param RELATIONSHIP Relationship at which the hash table is resized. if amount of elements is RELATIONSHIP   * times bigger than the hash table, table is resized to solve this condition. if RELATIONSHIP is zero, table is always MIN_HASH_TABLE_POWER.   * -*/ + */  template <class TKey, class TData, class Hasher = HashMapHasherDefault, class Comparator = HashMapComparatorDefault<TKey>, uint8_t MIN_HASH_TABLE_POWER = 3, uint8_t RELATIONSHIP = 8>  class HashMap { @@ -62,7 +62,9 @@ public:  		TKey key;  		TData data; -		Pair() {} +		Pair(const TKey &p_key) : +				key(p_key), +				data() {}  		Pair(const TKey &p_key, const TData &p_data) :  				key(p_key),  				data(p_data) { @@ -90,6 +92,12 @@ public:  		const TData &value() const {  			return pair.value();  		} + +		Element(const TKey &p_key) : +				pair(p_key) {} +		Element(const Element &p_other) : +				hash(p_other.hash), +				pair(p_other.pair.key, p_other.pair.data) {}  	};  private: @@ -192,14 +200,12 @@ private:  	Element *create_element(const TKey &p_key) {  		/* if element doesn't exist, create it */ -		Element *e = memnew(Element); +		Element *e = memnew(Element(p_key));  		ERR_FAIL_COND_V_MSG(!e, nullptr, "Out of memory.");  		uint32_t hash = Hasher::hash(p_key);  		uint32_t index = hash & ((1 << hash_table_power) - 1);  		e->next = hash_table[index];  		e->hash = hash; -		e->pair.key = p_key; -		e->pair.data = TData();  		hash_table[index] = e;  		elements++; @@ -228,9 +234,7 @@ private:  			const Element *e = p_t.hash_table[i];  			while (e) { -				Element *le = memnew(Element); /* local element */ - -				*le = *e; /* copy data */ +				Element *le = memnew(Element(*e)); /* local element */  				/* add to list and reassign pointers */  				le->next = hash_table[i]; @@ -454,8 +458,8 @@ public:  	 *  	 * 		print( *k );  	 * 	} -         * -	*/ +	 * +	 */  	const TKey *next(const TKey *p_key) const {  		if (unlikely(!hash_table)) {  			return nullptr; diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h index 2e932f9f26..c1a7c4146e 100644 --- a/core/templates/hashfuncs.h +++ b/core/templates/hashfuncs.h @@ -74,6 +74,13 @@ static inline uint32_t hash_djb2_one_32(uint32_t p_in, uint32_t p_prev = 5381) {  	return ((p_prev << 5) + p_prev) + p_in;  } +/** + * Thomas Wang's 64-bit to 32-bit Hash function: + * https://web.archive.org/web/20071223173210/https:/www.concentric.net/~Ttwang/tech/inthash.htm + * + * @param p_int - 64-bit unsigned integer key to be hashed + * @return unsigned 32-bit value representing hashcode + */  static inline uint32_t hash_one_uint64(const uint64_t p_int) {  	uint64_t v = p_int;  	v = (~v) + (v << 18); // v = (v << 18) - v - 1; @@ -82,7 +89,7 @@ static inline uint32_t hash_one_uint64(const uint64_t p_int) {  	v = v ^ (v >> 11);  	v = v + (v << 6);  	v = v ^ (v >> 22); -	return (int)v; +	return uint32_t(v);  }  static inline uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev = 5381) { diff --git a/core/templates/list.h b/core/templates/list.h index c2e17a2f6f..afbed998c2 100644 --- a/core/templates/list.h +++ b/core/templates/list.h @@ -249,29 +249,29 @@ private:  public:  	/** -	* return a const iterator to the beginning of the list. -	*/ +	 * return a const iterator to the beginning of the list. +	 */  	_FORCE_INLINE_ const Element *front() const {  		return _data ? _data->first : nullptr;  	}  	/** -	* return an iterator to the beginning of the list. -	*/ +	 * return an iterator to the beginning of the list. +	 */  	_FORCE_INLINE_ Element *front() {  		return _data ? _data->first : nullptr;  	}  	/** - 	* return a const iterator to the last member of the list. -	*/ +	 * return a const iterator to the last member of the list. +	 */  	_FORCE_INLINE_ const Element *back() const {  		return _data ? _data->last : nullptr;  	}  	/** - 	* return an iterator to the last member of the list. -	*/ +	 * return an iterator to the last member of the list. +	 */  	_FORCE_INLINE_ Element *back() {  		return _data ? _data->last : nullptr;  	} diff --git a/core/templates/local_vector.h b/core/templates/local_vector.h index 5704b8f230..4ec08821f8 100644 --- a/core/templates/local_vector.h +++ b/core/templates/local_vector.h @@ -70,7 +70,7 @@ public:  		}  	} -	void remove(U p_index) { +	void remove_at(U p_index) {  		ERR_FAIL_UNSIGNED_INDEX(p_index, count);  		count--;  		for (U i = p_index; i < count; i++) { @@ -83,7 +83,7 @@ public:  	/// Removes the item copying the last value into the position of the one to  	/// remove. It's generally faster than `remove`. -	void remove_unordered(U p_index) { +	void remove_at_unordered(U p_index) {  		ERR_FAIL_INDEX(p_index, count);  		count--;  		if (count > p_index) { @@ -97,7 +97,7 @@ public:  	void erase(const T &p_val) {  		int64_t idx = find(p_val);  		if (idx >= 0) { -			remove(idx); +			remove_at(idx);  		}  	} @@ -234,19 +234,17 @@ public:  			data[i] = p_from.data[i];  		}  	} -	inline LocalVector &operator=(const LocalVector &p_from) { +	inline void operator=(const LocalVector &p_from) {  		resize(p_from.size());  		for (U i = 0; i < p_from.count; i++) {  			data[i] = p_from.data[i];  		} -		return *this;  	} -	inline LocalVector &operator=(const Vector<T> &p_from) { +	inline void operator=(const Vector<T> &p_from) {  		resize(p_from.size());  		for (U i = 0; i < count; i++) {  			data[i] = p_from[i];  		} -		return *this;  	}  	_FORCE_INLINE_ ~LocalVector() { diff --git a/core/templates/oa_hash_map.h b/core/templates/oa_hash_map.h index 025cc30db4..9dab36e343 100644 --- a/core/templates/oa_hash_map.h +++ b/core/templates/oa_hash_map.h @@ -353,7 +353,7 @@ public:  		(*this) = p_other;  	} -	OAHashMap &operator=(const OAHashMap &p_other) { +	void operator=(const OAHashMap &p_other) {  		if (capacity != 0) {  			clear();  		} @@ -363,7 +363,6 @@ public:  		for (Iterator it = p_other.iter(); it.valid; it = p_other.next_iter(it)) {  			set(*it.key, *it.value);  		} -		return *this;  	}  	OAHashMap(uint32_t p_initial_capacity = 64) { diff --git a/core/templates/ordered_hash_map.h b/core/templates/ordered_hash_map.h index 7a17eeb644..928072bb18 100644 --- a/core/templates/ordered_hash_map.h +++ b/core/templates/ordered_hash_map.h @@ -85,11 +85,10 @@ public:  				next_element(other.next_element) {  		} -		Element &operator=(const Element &other) { +		void operator=(const Element &other) {  			list_element = other.list_element;  			next_element = other.next_element;  			prev_element = other.prev_element; -			return *this;  		}  		_FORCE_INLINE_ bool operator==(const Element &p_other) const { @@ -145,9 +144,8 @@ public:  				list_element(other.list_element) {  		} -		ConstElement &operator=(const ConstElement &other) { +		void operator=(const ConstElement &other) {  			list_element = other.list_element; -			return *this;  		}  		ConstElement next() const { @@ -207,8 +205,12 @@ public:  			(*list_element)->get().second = p_value;  			return Element(*list_element);  		} -		typename InternalList::Element *new_element = list.push_back(Pair<const K *, V>(nullptr, p_value)); +		// Incorrectly set the first value of the pair with a value that will +		// be invalid as soon as we leave this function... +		typename InternalList::Element *new_element = list.push_back(Pair<const K *, V>(&p_key, p_value)); +		// ...this is needed here in case the hashmap recursively reference itself...  		typename InternalMap::Element *e = map.set(p_key, new_element); +		// ...now we can set the right value !  		new_element->get().first = &e->key();  		return Element(new_element); diff --git a/core/templates/pooled_list.h b/core/templates/pooled_list.h index b4a6d2d1dd..b139dadb75 100644 --- a/core/templates/pooled_list.h +++ b/core/templates/pooled_list.h @@ -28,13 +28,16 @@  /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */  /*************************************************************************/ -#pragma once +#ifndef POOLED_LIST_H +#define POOLED_LIST_H + +#include "core/templates/local_vector.h"  // Simple template to provide a pool with O(1) allocate and free.  // The freelist could alternatively be a linked list placed within the unused elements  // to use less memory, however a separate freelist is probably more cache friendly. - -// NOTE : Take great care when using this with non POD types. The construction and destruction +// +// NOTE: Take great care when using this with non POD types. The construction and destruction  // is done in the LocalVector, NOT as part of the pool. So requesting a new item does not guarantee  // a constructor is run, and free does not guarantee a destructor.  // You should generally handle clearing @@ -42,9 +45,6 @@  // This is by design for fastest use in the BVH. If you want a more general pool  // that does call constructors / destructors on request / free, this should probably be  // a separate template. - -#include "core/templates/local_vector.h" -  template <class T, bool force_trivial = false>  class PooledList {  	LocalVector<T, uint32_t, force_trivial> list; @@ -93,3 +93,5 @@ public:  		_used_size--;  	}  }; + +#endif // POOLED_LIST_H diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h index e947d2211c..71d41eacc4 100644 --- a/core/templates/rid_owner.h +++ b/core/templates/rid_owner.h @@ -151,7 +151,7 @@ public:  		return _allocate_rid();  	} -	_FORCE_INLINE_ T *getornull(const RID &p_rid, bool p_initialize = false) { +	_FORCE_INLINE_ T *get_or_null(const RID &p_rid, bool p_initialize = false) {  		if (p_rid == RID()) {  			return nullptr;  		} @@ -210,12 +210,12 @@ public:  		return ptr;  	}  	void initialize_rid(RID p_rid) { -		T *mem = getornull(p_rid, true); +		T *mem = get_or_null(p_rid, true);  		ERR_FAIL_COND(!mem);  		memnew_placement(mem, T);  	}  	void initialize_rid(RID p_rid, const T &p_value) { -		T *mem = getornull(p_rid, true); +		T *mem = get_or_null(p_rid, true);  		ERR_FAIL_COND(!mem);  		memnew_placement(mem, T(p_value));  	} @@ -399,8 +399,8 @@ public:  		alloc.initialize_rid(p_rid, p_ptr);  	} -	_FORCE_INLINE_ T *getornull(const RID &p_rid) { -		T **ptr = alloc.getornull(p_rid); +	_FORCE_INLINE_ T *get_or_null(const RID &p_rid) { +		T **ptr = alloc.get_or_null(p_rid);  		if (unlikely(!ptr)) {  			return nullptr;  		} @@ -408,7 +408,7 @@ public:  	}  	_FORCE_INLINE_ void replace(const RID &p_rid, T *p_new_ptr) { -		T **ptr = alloc.getornull(p_rid); +		T **ptr = alloc.get_or_null(p_rid);  		ERR_FAIL_COND(!ptr);  		*ptr = p_new_ptr;  	} @@ -469,8 +469,8 @@ public:  		alloc.initialize_rid(p_rid, p_ptr);  	} -	_FORCE_INLINE_ T *getornull(const RID &p_rid) { -		return alloc.getornull(p_rid); +	_FORCE_INLINE_ T *get_or_null(const RID &p_rid) { +		return alloc.get_or_null(p_rid);  	}  	_FORCE_INLINE_ bool owns(const RID &p_rid) { diff --git a/core/templates/search_array.h b/core/templates/search_array.h new file mode 100644 index 0000000000..8efc32df82 --- /dev/null +++ b/core/templates/search_array.h @@ -0,0 +1,67 @@ +/*************************************************************************/ +/*  search_array.h                                                       */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2021 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 SEARCH_ARRAY_H +#define SEARCH_ARRAY_H + +#include <core/templates/sort_array.h> + +template <class T, class Comparator = _DefaultComparator<T>> +class SearchArray { +public: +	Comparator compare; + +	inline int bisect(const T *p_array, int p_len, const T &p_value, bool p_before) const { +		int lo = 0; +		int hi = p_len; +		if (p_before) { +			while (lo < hi) { +				const int mid = (lo + hi) / 2; +				if (compare(p_array[mid], p_value)) { +					lo = mid + 1; +				} else { +					hi = mid; +				} +			} +		} else { +			while (lo < hi) { +				const int mid = (lo + hi) / 2; +				if (compare(p_value, p_array[mid])) { +					hi = mid; +				} else { +					lo = mid + 1; +				} +			} +		} +		return lo; +	} +}; + +#endif // SEARCH_ARRAY_H diff --git a/core/templates/thread_work_pool.cpp b/core/templates/thread_work_pool.cpp index 17969a2c90..710f043a4a 100644 --- a/core/templates/thread_work_pool.cpp +++ b/core/templates/thread_work_pool.cpp @@ -47,7 +47,7 @@ void ThreadWorkPool::_thread_function(void *p_user) {  void ThreadWorkPool::init(int p_thread_count) {  	ERR_FAIL_COND(threads != nullptr);  	if (p_thread_count < 0) { -		p_thread_count = OS::get_singleton()->get_processor_count(); +		p_thread_count = OS::get_singleton()->get_default_thread_pool_size();  	}  	thread_count = p_thread_count; diff --git a/core/templates/thread_work_pool.h b/core/templates/thread_work_pool.h index b242648bc8..19096c496a 100644 --- a/core/templates/thread_work_pool.h +++ b/core/templates/thread_work_pool.h @@ -73,6 +73,7 @@ class ThreadWorkPool {  	ThreadData *threads = nullptr;  	uint32_t thread_count = 0; +	uint32_t threads_working = 0;  	BaseWork *current_work = nullptr;  	static void _thread_function(void *p_user); @@ -94,7 +95,9 @@ public:  		current_work = w; -		for (uint32_t i = 0; i < thread_count; i++) { +		threads_working = MIN(p_elements, thread_count); + +		for (uint32_t i = 0; i < threads_working; i++) {  			threads[i].work = w;  			threads[i].start.post();  		} @@ -117,19 +120,32 @@ public:  	void end_work() {  		ERR_FAIL_COND(current_work == nullptr); -		for (uint32_t i = 0; i < thread_count; i++) { +		for (uint32_t i = 0; i < threads_working; i++) {  			threads[i].completed.wait();  			threads[i].work = nullptr;  		} +		threads_working = 0;  		memdelete(current_work);  		current_work = nullptr;  	}  	template <class C, class M, class U>  	void do_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) { -		begin_work(p_elements, p_instance, p_method, p_userdata); -		end_work(); +		switch (p_elements) { +			case 0: +				// Nothing to do, so do nothing. +				break; +			case 1: +				// No value in pushing the work to another thread if it's a single job +				// and we're going to wait for it to finish. Just run it right here. +				(p_instance->*p_method)(0, p_userdata); +				break; +			default: +				// Multiple jobs to do; commence threaded business. +				begin_work(p_elements, p_instance, p_method, p_userdata); +				end_work(); +		}  	}  	_FORCE_INLINE_ int get_thread_count() const { return thread_count; } diff --git a/core/templates/vector.h b/core/templates/vector.h index 08cbef6ba4..2f51a83848 100644 --- a/core/templates/vector.h +++ b/core/templates/vector.h @@ -35,11 +35,12 @@   * @class Vector   * @author Juan Linietsky   * Vector container. Regular Vector Container. Use with care and for smaller arrays when possible. Use Vector for large arrays. -*/ + */  #include "core/error/error_macros.h"  #include "core/os/memory.h"  #include "core/templates/cowdata.h" +#include "core/templates/search_array.h"  #include "core/templates/sort_array.h"  template <class T> @@ -67,11 +68,11 @@ public:  	_FORCE_INLINE_ bool append(const T &p_elem) { return push_back(p_elem); } //alias  	void fill(T p_elem); -	void remove(int p_index) { _cowdata.remove(p_index); } +	void remove_at(int p_index) { _cowdata.remove_at(p_index); }  	void erase(const T &p_val) {  		int idx = find(p_val);  		if (idx >= 0) { -			remove(idx); +			remove_at(idx);  		}  	}  	void reverse(); @@ -92,7 +93,7 @@ public:  	void append_array(Vector<T> p_other); -	bool has(const T &p_val) { +	bool has(const T &p_val) const {  		return find(p_val, 0) != -1;  	} @@ -112,6 +113,11 @@ public:  		sort_custom<_DefaultComparator<T>>();  	} +	int bsearch(const T &p_value, bool p_before) { +		SearchArray<T> search; +		return search.bisect(ptrw(), size(), p_value, p_before); +	} +  	Vector<T> duplicate() {  		return *this;  	} @@ -126,9 +132,8 @@ public:  		insert(i, p_val);  	} -	inline Vector &operator=(const Vector &p_from) { +	inline void operator=(const Vector &p_from) {  		_cowdata._ref(p_from._cowdata); -		return *this;  	}  	Vector<uint8_t> to_byte_array() const { @@ -138,27 +143,28 @@ public:  		return ret;  	} -	Vector<T> subarray(int p_from, int p_to) const { -		if (p_from < 0) { -			p_from = size() + p_from; -		} -		if (p_to < 0) { -			p_to = size() + p_to; +	Vector<T> slice(int p_begin, int p_end) const { +		Vector<T> result; + +		if (p_end < 0) { +			p_end += size() + 1;  		} -		ERR_FAIL_INDEX_V(p_from, size(), Vector<T>()); -		ERR_FAIL_INDEX_V(p_to, size(), Vector<T>()); +		ERR_FAIL_INDEX_V(p_begin, size(), result); +		ERR_FAIL_INDEX_V(p_end, size() + 1, result); + +		ERR_FAIL_COND_V(p_begin > p_end, result); + +		int result_size = p_end - p_begin; +		result.resize(result_size); -		Vector<T> slice; -		int span = 1 + p_to - p_from; -		slice.resize(span); -		const T *r = ptr(); -		T *w = slice.ptrw(); -		for (int i = 0; i < span; ++i) { -			w[i] = r[p_from + i]; +		const T *const r = ptr(); +		T *const w = result.ptrw(); +		for (int i = 0; i < result_size; ++i) { +			w[i] = r[p_begin + i];  		} -		return slice; +		return result;  	}  	bool operator==(const Vector<T> &p_arr) const { @@ -229,7 +235,7 @@ public:  		_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return elem_ptr == b.elem_ptr; }  		_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return elem_ptr != b.elem_ptr; } -		ConstIterator(T *p_ptr) { elem_ptr = p_ptr; } +		ConstIterator(const T *p_ptr) { elem_ptr = p_ptr; }  		ConstIterator() {}  		ConstIterator(const ConstIterator &p_it) { elem_ptr = p_it.elem_ptr; } diff --git a/core/templates/vmap.h b/core/templates/vmap.h index 520e0b3720..0ff105ccbf 100644 --- a/core/templates/vmap.h +++ b/core/templates/vmap.h @@ -134,7 +134,7 @@ public:  		if (pos < 0) {  			return;  		} -		_cowdata.remove(pos); +		_cowdata.remove_at(pos);  	}  	int find(const T &p_val) const { @@ -193,9 +193,8 @@ public:  	_FORCE_INLINE_ VMap() {}  	_FORCE_INLINE_ VMap(const VMap &p_from) { _cowdata._ref(p_from._cowdata); } -	inline VMap &operator=(const VMap &p_from) { +	inline void operator=(const VMap &p_from) {  		_cowdata._ref(p_from._cowdata); -		return *this;  	}  }; diff --git a/core/templates/vset.h b/core/templates/vset.h index 6665651d42..94e7a17061 100644 --- a/core/templates/vset.h +++ b/core/templates/vset.h @@ -119,7 +119,7 @@ public:  		if (pos < 0) {  			return;  		} -		_data.remove(pos); +		_data.remove_at(pos);  	}  	int find(const T &p_val) const { diff --git a/core/typedefs.h b/core/typedefs.h index dde254af23..95bd423817 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -62,9 +62,9 @@  #endif  #endif -// Should always inline, except in debug builds because it makes debugging harder. +// Should always inline, except in dev builds because it makes debugging harder.  #ifndef _FORCE_INLINE_ -#ifdef DISABLE_FORCED_INLINE +#ifdef DEV_ENABLED  #define _FORCE_INLINE_ inline  #else  #define _FORCE_INLINE_ _ALWAYS_INLINE_ @@ -91,8 +91,8 @@  #define ABS(m_v) (((m_v) < 0) ? (-(m_v)) : (m_v))  #endif -#ifndef SGN -#define SGN(m_v) (((m_v) == 0) ? (0.0) : (((m_v) < 0) ? (-1.0) : (+1.0))) +#ifndef SIGN +#define SIGN(m_v) (((m_v) == 0) ? (0.0) : (((m_v) < 0) ? (-1.0) : (+1.0)))  #endif  #ifndef MIN @@ -277,6 +277,9 @@ struct BuildIndexSequence : BuildIndexSequence<N - 1, N - 1, Is...> {};  template <size_t... Is>  struct BuildIndexSequence<0, Is...> : IndexSequence<Is...> {}; +// Limit the depth of recursive algorithms when dealing with Array/Dictionary +#define MAX_RECURSION 100 +  #ifdef DEBUG_ENABLED  #define DEBUG_METHODS_ENABLED  #endif diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 8373cbd4e8..45f2e0c5ac 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -34,6 +34,7 @@  #include "core/object/class_db.h"  #include "core/object/script_language.h"  #include "core/templates/hashfuncs.h" +#include "core/templates/search_array.h"  #include "core/templates/vector.h"  #include "core/variant/callable.h"  #include "core/variant/variant.h" @@ -96,11 +97,38 @@ void Array::clear() {  }  bool Array::operator==(const Array &p_array) const { -	return _p == p_array._p; +	return recursive_equal(p_array, 0);  }  bool Array::operator!=(const Array &p_array) const { -	return !operator==(p_array); +	return !recursive_equal(p_array, 0); +} + +bool Array::recursive_equal(const Array &p_array, int recursion_count) const { +	// Cheap checks +	if (_p == p_array._p) { +		return true; +	} +	const Vector<Variant> &a1 = _p->array; +	const Vector<Variant> &a2 = p_array._p->array; +	const int size = a1.size(); +	if (size != a2.size()) { +		return false; +	} + +	// Heavy O(n) check +	if (recursion_count > MAX_RECURSION) { +		ERR_PRINT("Max recursion reached"); +		return true; +	} +	recursion_count++; +	for (int i = 0; i < size; i++) { +		if (!a1[i].hash_compare(a2[i], recursion_count)) { +			return false; +		} +	} + +	return true;  }  bool Array::operator<(const Array &p_array) const { @@ -131,10 +159,20 @@ bool Array::operator>=(const Array &p_array) const {  }  uint32_t Array::hash() const { -	uint32_t h = hash_djb2_one_32(0); +	return recursive_hash(0); +} +uint32_t Array::recursive_hash(int recursion_count) const { +	if (recursion_count > MAX_RECURSION) { +		ERR_PRINT("Max recursion reached"); +		return 0; +	} + +	uint32_t h = hash_djb2_one_32(Variant::ARRAY); + +	recursion_count++;  	for (int i = 0; i < _p->array.size(); i++) { -		h = hash_djb2_one_32(_p->array[i].hash(), h); +		h = hash_djb2_one_32(_p->array[i].recursive_hash(recursion_count), h);  	}  	return h;  } @@ -284,8 +322,8 @@ bool Array::has(const Variant &p_value) const {  	return _p->array.find(p_value, 0) != -1;  } -void Array::remove(int p_pos) { -	_p->array.remove(p_pos); +void Array::remove_at(int p_pos) { +	_p->array.remove_at(p_pos);  }  void Array::set(int p_idx, const Variant &p_value) { @@ -299,66 +337,58 @@ const Variant &Array::get(int p_idx) const {  }  Array Array::duplicate(bool p_deep) const { +	return recursive_duplicate(p_deep, 0); +} + +Array Array::recursive_duplicate(bool p_deep, int recursion_count) const {  	Array new_arr; + +	if (recursion_count > MAX_RECURSION) { +		ERR_PRINT("Max recursion reached"); +		return new_arr; +	} +  	int element_count = size();  	new_arr.resize(element_count);  	new_arr._p->typed = _p->typed; -	for (int i = 0; i < element_count; i++) { -		new_arr[i] = p_deep ? get(i).duplicate(p_deep) : get(i); +	if (p_deep) { +		recursion_count++; +		for (int i = 0; i < element_count; i++) { +			new_arr[i] = get(i).recursive_duplicate(true, recursion_count); +		} +	} else { +		for (int i = 0; i < element_count; i++) { +			new_arr[i] = get(i); +		}  	}  	return new_arr;  } -int Array::_clamp_slice_index(int p_index) const { -	int arr_size = size(); -	int fixed_index = CLAMP(p_index, -arr_size, arr_size - 1); -	if (fixed_index < 0) { -		fixed_index = arr_size + fixed_index; -	} -	return fixed_index; -} - -Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const { // like python, but inclusive on upper bound - -	Array new_arr; +Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const { +	Array result; -	ERR_FAIL_COND_V_MSG(p_step == 0, new_arr, "Array slice step size cannot be zero."); +	ERR_FAIL_COND_V_MSG(p_step == 0, result, "Slice step cannot be zero."); -	if (is_empty()) { // Don't try to slice empty arrays. -		return new_arr; -	} -	if (p_step > 0) { -		if (p_begin >= size() || p_end < -size()) { -			return new_arr; -		} -	} else { // p_step < 0 -		if (p_begin < -size() || p_end >= size()) { -			return new_arr; -		} +	if (p_end < 0) { +		p_end += size() + 1;  	} -	int begin = _clamp_slice_index(p_begin); -	int end = _clamp_slice_index(p_end); +	ERR_FAIL_INDEX_V(p_begin, size(), result); +	ERR_FAIL_INDEX_V(p_end, size() + 1, result); -	int new_arr_size = MAX(((end - begin + p_step) / p_step), 0); -	new_arr.resize(new_arr_size); +	ERR_FAIL_COND_V_MSG(p_step > 0 && p_begin > p_end, result, "Slice is positive, but bounds is decreasing"); +	ERR_FAIL_COND_V_MSG(p_step < 0 && p_begin < p_end, result, "Slice is negative, but bounds is increasing"); -	if (p_step > 0) { -		int dest_idx = 0; -		for (int idx = begin; idx <= end; idx += p_step) { -			ERR_FAIL_COND_V_MSG(dest_idx < 0 || dest_idx >= new_arr_size, Array(), "Bug in Array slice()"); -			new_arr[dest_idx++] = p_deep ? get(idx).duplicate(p_deep) : get(idx); -		} -	} else { // p_step < 0 -		int dest_idx = 0; -		for (int idx = begin; idx >= end; idx += p_step) { -			ERR_FAIL_COND_V_MSG(dest_idx < 0 || dest_idx >= new_arr_size, Array(), "Bug in Array slice()"); -			new_arr[dest_idx++] = p_deep ? get(idx).duplicate(p_deep) : get(idx); -		} +	int result_size = (p_end - p_begin) / p_step; +	result.resize(result_size); + +	for (int src_idx = p_begin, dest_idx = 0; dest_idx < result_size; ++dest_idx) { +		result[dest_idx] = p_deep ? get(src_idx).duplicate(true) : get(src_idx); +		src_idx += p_step;  	} -	return new_arr; +	return result;  }  Array Array::filter(const Callable &p_callable) const { @@ -484,44 +514,19 @@ void Array::shuffle() {  	}  } -template <typename Less> -_FORCE_INLINE_ int bisect(const Vector<Variant> &p_array, const Variant &p_value, bool p_before, const Less &p_less) { -	int lo = 0; -	int hi = p_array.size(); -	if (p_before) { -		while (lo < hi) { -			const int mid = (lo + hi) / 2; -			if (p_less(p_array.get(mid), p_value)) { -				lo = mid + 1; -			} else { -				hi = mid; -			} -		} -	} else { -		while (lo < hi) { -			const int mid = (lo + hi) / 2; -			if (p_less(p_value, p_array.get(mid))) { -				hi = mid; -			} else { -				lo = mid + 1; -			} -		} -	} -	return lo; -} -  int Array::bsearch(const Variant &p_value, bool p_before) {  	ERR_FAIL_COND_V(!_p->typed.validate(p_value, "binary search"), -1); -	return bisect(_p->array, p_value, p_before, _ArrayVariantSort()); +	SearchArray<Variant, _ArrayVariantSort> avs; +	return avs.bisect(_p->array.ptrw(), _p->array.size(), p_value, p_before);  }  int Array::bsearch_custom(const Variant &p_value, Callable p_callable, bool p_before) {  	ERR_FAIL_COND_V(!_p->typed.validate(p_value, "custom binary search"), -1); -	_ArrayVariantSortCustom less; -	less.func = p_callable; +	SearchArray<Variant, _ArrayVariantSortCustom> avs; +	avs.compare.func = p_callable; -	return bisect(_p->array, p_value, p_before, less); +	return avs.bisect(_p->array.ptrw(), _p->array.size(), p_value, p_before);  }  void Array::reverse() { @@ -546,7 +551,7 @@ Variant Array::pop_back() {  Variant Array::pop_front() {  	if (!_p->array.is_empty()) {  		const Variant ret = _p->array.get(0); -		_p->array.remove(0); +		_p->array.remove_at(0);  		return ret;  	}  	return Variant(); @@ -573,7 +578,7 @@ Variant Array::pop_at(int p_pos) {  					_p->array.size()));  	const Variant ret = _p->array.get(p_pos); -	_p->array.remove(p_pos); +	_p->array.remove_at(p_pos);  	return ret;  } diff --git a/core/variant/array.h b/core/variant/array.h index 4a1b25c4a9..6a68a9b9ff 100644 --- a/core/variant/array.h +++ b/core/variant/array.h @@ -44,8 +44,6 @@ class Array {  	void _ref(const Array &p_from) const;  	void _unref() const; -	inline int _clamp_slice_index(int p_index) const; -  protected:  	Array(const Array &p_base, uint32_t p_type, const StringName &p_class_name, const Variant &p_script);  	bool _assign(const Array &p_array); @@ -63,8 +61,10 @@ public:  	bool operator==(const Array &p_array) const;  	bool operator!=(const Array &p_array) const; +	bool recursive_equal(const Array &p_array, int recursion_count) const;  	uint32_t hash() const; +	uint32_t recursive_hash(int recursion_count) const;  	void operator=(const Array &p_array);  	void push_back(const Variant &p_value); @@ -73,7 +73,7 @@ public:  	Error resize(int p_new_size);  	Error insert(int p_pos, const Variant &p_value); -	void remove(int p_pos); +	void remove_at(int p_pos);  	void fill(const Variant &p_value);  	Variant front() const; @@ -100,6 +100,7 @@ public:  	Variant pop_at(int p_pos);  	Array duplicate(bool p_deep = false) const; +	Array recursive_duplicate(bool p_deep, int recursion_count) const;  	Array slice(int p_begin, int p_end, int p_step = 1, bool p_deep = false) const;  	Array filter(const Callable &p_callable) const; diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index 3b2c837096..3fb4af4944 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -80,14 +80,18 @@ struct VariantCaster<const T &> {  		}                                                                    \  		typedef int64_t EncodeT;                                             \  		_FORCE_INLINE_ static void encode(m_enum p_val, const void *p_ptr) { \ -			*(int64_t *)p_ptr = p_val;                                       \ +			*(int64_t *)p_ptr = (int64_t)p_val;                              \  		}                                                                    \  	};  // Object enum casts must go here  VARIANT_ENUM_CAST(Object::ConnectFlags); +VARIANT_ENUM_CAST(Vector2::Axis); +VARIANT_ENUM_CAST(Vector2i::Axis);  VARIANT_ENUM_CAST(Vector3::Axis); +VARIANT_ENUM_CAST(Vector3i::Axis); +VARIANT_ENUM_CAST(Basis::EulerOrder);  VARIANT_ENUM_CAST(Error);  VARIANT_ENUM_CAST(Side); @@ -102,9 +106,9 @@ VARIANT_ENUM_CAST(KeyModifierMask);  VARIANT_ENUM_CAST(MIDIMessage);  VARIANT_ENUM_CAST(MouseButton);  VARIANT_ENUM_CAST(Orientation); -VARIANT_ENUM_CAST(HAlign); -VARIANT_ENUM_CAST(VAlign); -VARIANT_ENUM_CAST(InlineAlign); +VARIANT_ENUM_CAST(HorizontalAlignment); +VARIANT_ENUM_CAST(VerticalAlignment); +VARIANT_ENUM_CAST(InlineAlignment);  VARIANT_ENUM_CAST(PropertyHint);  VARIANT_ENUM_CAST(PropertyUsageFlags);  VARIANT_ENUM_CAST(Variant::Type); @@ -563,13 +567,11 @@ void call_with_validated_variant_args_static_method_ret(R (*p_method)(P...), con  // GCC raises "parameter 'p_args' set but not used" when P = {},  // it's not clever enough to treat other P values as making this branch valid. -#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__) +#if defined(__GNUC__) && !defined(__clang__)  #pragma GCC diagnostic push  #pragma GCC diagnostic ignored "-Wunused-but-set-parameter"  #endif -#ifdef DEBUG_METHODS_ENABLED -  template <class Q>  void call_get_argument_type_helper(int p_arg, int &index, Variant::Type &type) {  	if (p_arg == index) { @@ -608,6 +610,7 @@ void call_get_argument_type_info(int p_arg, PropertyInfo &info) {  	(void)index; // Suppress GCC warning.  } +#ifdef DEBUG_METHODS_ENABLED  template <class Q>  void call_get_argument_metadata_helper(int p_arg, int &index, GodotTypeInfo::Metadata &md) {  	if (p_arg == index) { @@ -629,13 +632,6 @@ GodotTypeInfo::Metadata call_get_argument_metadata(int p_arg) {  	return md;  } -#else - -template <class... P> -Variant::Type call_get_argument_type(int p_arg) { -	return Variant::NIL; -} -  #endif // DEBUG_METHODS_ENABLED  ////////////////////// @@ -915,7 +911,7 @@ void call_with_variant_args_static_dv(void (*p_method)(P...), const Variant **p_  	call_with_variant_args_static(p_method, args, r_error, BuildIndexSequence<sizeof...(P)>{});  } -#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__) +#if defined(__GNUC__) && !defined(__clang__)  #pragma GCC diagnostic pop  #endif diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp index f487e718f4..dcded6e61f 100644 --- a/core/variant/callable.cpp +++ b/core/variant/callable.cpp @@ -377,11 +377,11 @@ Error Signal::emit(const Variant **p_arguments, int p_argcount) const {  	return obj->emit_signal(name, p_arguments, p_argcount);  } -Error Signal::connect(const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) { +Error Signal::connect(const Callable &p_callable, uint32_t p_flags) {  	Object *object = get_object();  	ERR_FAIL_COND_V(!object, ERR_UNCONFIGURED); -	return object->connect(name, p_callable, p_binds, p_flags); +	return object->connect(name, p_callable, varray(), p_flags);  }  void Signal::disconnect(const Callable &p_callable) { diff --git a/core/variant/callable.h b/core/variant/callable.h index 52094af3aa..de886492ea 100644 --- a/core/variant/callable.h +++ b/core/variant/callable.h @@ -159,7 +159,7 @@ public:  	operator String() const;  	Error emit(const Variant **p_arguments, int p_argcount) const; -	Error connect(const Callable &p_callable, const Vector<Variant> &p_binds = Vector<Variant>(), uint32_t p_flags = 0); +	Error connect(const Callable &p_callable, uint32_t p_flags = 0);  	void disconnect(const Callable &p_callable);  	bool is_connected(const Callable &p_callable) const; diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp index 10446a5ec1..56eda6e703 100644 --- a/core/variant/callable_bind.cpp +++ b/core/variant/callable_bind.cpp @@ -169,7 +169,8 @@ CallableCustomUnbind::~CallableCustomUnbind() {  }  Callable callable_bind(const Callable &p_callable, const Variant &p_arg1) { -	return p_callable.bind((const Variant **)&p_arg1, 1); +	const Variant *args[1] = { &p_arg1 }; +	return p_callable.bind(args, 1);  }  Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2) { diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index 07b3a9a675..24d21386a7 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -188,11 +188,35 @@ bool Dictionary::erase(const Variant &p_key) {  }  bool Dictionary::operator==(const Dictionary &p_dictionary) const { -	return _p == p_dictionary._p; +	return recursive_equal(p_dictionary, 0);  }  bool Dictionary::operator!=(const Dictionary &p_dictionary) const { -	return _p != p_dictionary._p; +	return !recursive_equal(p_dictionary, 0); +} + +bool Dictionary::recursive_equal(const Dictionary &p_dictionary, int recursion_count) const { +	// Cheap checks +	if (_p == p_dictionary._p) { +		return true; +	} +	if (_p->variant_map.size() != p_dictionary._p->variant_map.size()) { +		return false; +	} + +	// Heavy O(n) check +	if (recursion_count > MAX_RECURSION) { +		ERR_PRINT("Max recursion reached"); +		return true; +	} +	recursion_count++; +	for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement this_E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->front(); this_E; this_E = this_E.next()) { +		OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement other_E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&p_dictionary._p->variant_map)->find(this_E.key()); +		if (!other_E || !this_E.value().hash_compare(other_E.value(), recursion_count)) { +			return false; +		} +	} +	return true;  }  void Dictionary::_ref(const Dictionary &p_from) const { @@ -225,11 +249,21 @@ void Dictionary::_unref() const {  }  uint32_t Dictionary::hash() const { +	return recursive_hash(0); +} + +uint32_t Dictionary::recursive_hash(int recursion_count) const { +	if (recursion_count > MAX_RECURSION) { +		ERR_PRINT("Max recursion reached"); +		return 0; +	} +  	uint32_t h = hash_djb2_one_32(Variant::DICTIONARY); +	recursion_count++;  	for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { -		h = hash_djb2_one_32(E.key().hash(), h); -		h = hash_djb2_one_32(E.value().hash(), h); +		h = hash_djb2_one_32(E.key().recursive_hash(recursion_count), h); +		h = hash_djb2_one_32(E.value().recursive_hash(recursion_count), h);  	}  	return h; @@ -286,10 +320,26 @@ const Variant *Dictionary::next(const Variant *p_key) const {  }  Dictionary Dictionary::duplicate(bool p_deep) const { +	return recursive_duplicate(p_deep, 0); +} + +Dictionary Dictionary::recursive_duplicate(bool p_deep, int recursion_count) const {  	Dictionary n; -	for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { -		n[E.key()] = p_deep ? E.value().duplicate(true) : E.value(); +	if (recursion_count > MAX_RECURSION) { +		ERR_PRINT("Max recursion reached"); +		return n; +	} + +	if (p_deep) { +		recursion_count++; +		for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { +			n[E.key().recursive_duplicate(true, recursion_count)] = E.value().recursive_duplicate(true, recursion_count); +		} +	} else { +		for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { +			n[E.key()] = E.value(); +		}  	}  	return n; diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h index 4067ff9fd9..f8a2a7573f 100644 --- a/core/variant/dictionary.h +++ b/core/variant/dictionary.h @@ -70,8 +70,10 @@ public:  	bool operator==(const Dictionary &p_dictionary) const;  	bool operator!=(const Dictionary &p_dictionary) const; +	bool recursive_equal(const Dictionary &p_dictionary, int recursion_count) const;  	uint32_t hash() const; +	uint32_t recursive_hash(int recursion_count) const;  	void operator=(const Dictionary &p_dictionary);  	const Variant *next(const Variant *p_key = nullptr) const; @@ -80,6 +82,7 @@ public:  	Array values() const;  	Dictionary duplicate(bool p_deep = false) const; +	Dictionary recursive_duplicate(bool p_deep, int recursion_count) const;  	const void *id() const; diff --git a/core/variant/native_ptr.h b/core/variant/native_ptr.h index b4ec0df7d6..913d4d8f7c 100644 --- a/core/variant/native_ptr.h +++ b/core/variant/native_ptr.h @@ -55,7 +55,7 @@ struct GDNativePtr {  #define GDVIRTUAL_NATIVE_PTR(m_type)                                  \  	template <>                                                       \ -	struct GDNativeConstPtr<m_type> {                                 \ +	struct GDNativeConstPtr<const m_type> {                           \  		const m_type *data = nullptr;                                 \  		GDNativeConstPtr(const m_type *p_assign) { data = p_assign; } \  		static const char *get_name() { return "const " #m_type; }    \ @@ -117,6 +117,7 @@ GDVIRTUAL_NATIVE_PTR(char16_t)  GDVIRTUAL_NATIVE_PTR(char32_t)  GDVIRTUAL_NATIVE_PTR(wchar_t)  GDVIRTUAL_NATIVE_PTR(uint8_t) +GDVIRTUAL_NATIVE_PTR(uint8_t *)  GDVIRTUAL_NATIVE_PTR(int8_t)  GDVIRTUAL_NATIVE_PTR(uint16_t)  GDVIRTUAL_NATIVE_PTR(int16_t) diff --git a/core/variant/type_info.h b/core/variant/type_info.h index b70d29bbac..2c6b82d25f 100644 --- a/core/variant/type_info.h +++ b/core/variant/type_info.h @@ -60,7 +60,7 @@ struct TypeInherits {  	static char (&test(...))[2];  	static bool const value = sizeof(test(get_d())) == sizeof(char) && -							  !TypesAreSame<B volatile const, void volatile const>::value; +			!TypesAreSame<B volatile const, void volatile const>::value;  };  namespace GodotTypeInfo { diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index c5cada674f..c43ff8626e 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -313,7 +313,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {  		case BASIS: {  			static const Type valid[] = {  				QUATERNION, -				VECTOR3,  				NIL  			}; @@ -620,7 +619,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type  		case BASIS: {  			static const Type valid[] = {  				QUATERNION, -				VECTOR3,  				NIL  			}; @@ -786,16 +784,11 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type  }  bool Variant::operator==(const Variant &p_variant) const { -	if (type != p_variant.type) { //evaluation of operator== needs to be more strict -		return false; -	} -	bool v; -	Variant r; -	evaluate(OP_EQUAL, *this, p_variant, r, v); -	return r; +	return hash_compare(p_variant);  }  bool Variant::operator!=(const Variant &p_variant) const { +	// Don't use `!hash_compare(p_variant)` given it makes use of OP_EQUAL  	if (type != p_variant.type) { //evaluation of operator== needs to be more strict  		return true;  	} @@ -1619,12 +1612,23 @@ struct _VariantStrPair {  };  Variant::operator String() const { -	List<const void *> stack; +	return stringify(0); +} -	return stringify(stack); +template <class T> +String stringify_vector(const T &vec, int recursion_count) { +	String str("["); +	for (int i = 0; i < vec.size(); i++) { +		if (i > 0) { +			str += ", "; +		} +		str = str + Variant(vec[i]).stringify(recursion_count); +	} +	str += "]"; +	return str;  } -String Variant::stringify(List<const void *> &stack) const { +String Variant::stringify(int recursion_count) const {  	switch (type) {  		case NIL:  			return "null"; @@ -1668,23 +1672,22 @@ String Variant::stringify(List<const void *> &stack) const {  			return operator Color();  		case DICTIONARY: {  			const Dictionary &d = *reinterpret_cast<const Dictionary *>(_data._mem); -			if (stack.find(d.id())) { +			if (recursion_count > MAX_RECURSION) { +				ERR_PRINT("Max recursion reached");  				return "{...}";  			} -			stack.push_back(d.id()); - -			//const String *K=nullptr;  			String str("{");  			List<Variant> keys;  			d.get_key_list(&keys);  			Vector<_VariantStrPair> pairs; -			for (const Variant &E : keys) { +			recursion_count++; +			for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {  				_VariantStrPair sp; -				sp.key = E.stringify(stack); -				sp.value = d[E].stringify(stack); +				sp.key = E->get().stringify(recursion_count); +				sp.value = d[E->get()].stringify(recursion_count);  				pairs.push_back(sp);  			} @@ -1699,111 +1702,43 @@ String Variant::stringify(List<const void *> &stack) const {  			}  			str += "}"; -			stack.erase(d.id());  			return str;  		} break;  		case PACKED_VECTOR2_ARRAY: { -			Vector<Vector2> vec = operator Vector<Vector2>(); -			String str("["); -			for (int i = 0; i < vec.size(); i++) { -				if (i > 0) { -					str += ", "; -				} -				str = str + Variant(vec[i]); -			} -			str += "]"; -			return str; +			return stringify_vector(operator Vector<Vector2>(), recursion_count);  		} break;  		case PACKED_VECTOR3_ARRAY: { -			Vector<Vector3> vec = operator Vector<Vector3>(); -			String str("["); -			for (int i = 0; i < vec.size(); i++) { -				if (i > 0) { -					str += ", "; -				} -				str = str + Variant(vec[i]); -			} -			str += "]"; -			return str; +			return stringify_vector(operator Vector<Vector3>(), recursion_count); +		} break; +		case PACKED_COLOR_ARRAY: { +			return stringify_vector(operator Vector<Color>(), recursion_count);  		} break;  		case PACKED_STRING_ARRAY: { -			Vector<String> vec = operator Vector<String>(); -			String str("["); -			for (int i = 0; i < vec.size(); i++) { -				if (i > 0) { -					str += ", "; -				} -				str = str + vec[i]; -			} -			str += "]"; -			return str; +			return stringify_vector(operator Vector<String>(), recursion_count); +		} break; +		case PACKED_BYTE_ARRAY: { +			return stringify_vector(operator Vector<uint8_t>(), recursion_count);  		} break;  		case PACKED_INT32_ARRAY: { -			Vector<int32_t> vec = operator Vector<int32_t>(); -			String str("["); -			for (int i = 0; i < vec.size(); i++) { -				if (i > 0) { -					str += ", "; -				} -				str = str + itos(vec[i]); -			} -			str += "]"; -			return str; +			return stringify_vector(operator Vector<int32_t>(), recursion_count);  		} break;  		case PACKED_INT64_ARRAY: { -			Vector<int64_t> vec = operator Vector<int64_t>(); -			String str("["); -			for (int i = 0; i < vec.size(); i++) { -				if (i > 0) { -					str += ", "; -				} -				str = str + itos(vec[i]); -			} -			str += "]"; -			return str; +			return stringify_vector(operator Vector<int64_t>(), recursion_count);  		} break;  		case PACKED_FLOAT32_ARRAY: { -			Vector<float> vec = operator Vector<float>(); -			String str("["); -			for (int i = 0; i < vec.size(); i++) { -				if (i > 0) { -					str += ", "; -				} -				str = str + rtos(vec[i]); -			} -			str += "]"; -			return str; +			return stringify_vector(operator Vector<float>(), recursion_count);  		} break;  		case PACKED_FLOAT64_ARRAY: { -			Vector<double> vec = operator Vector<double>(); -			String str("["); -			for (int i = 0; i < vec.size(); i++) { -				if (i > 0) { -					str += ", "; -				} -				str = str + rtos(vec[i]); -			} -			str += "]"; -			return str; +			return stringify_vector(operator Vector<double>(), recursion_count);  		} break;  		case ARRAY: {  			Array arr = operator Array(); -			if (stack.find(arr.id())) { +			if (recursion_count > MAX_RECURSION) { +				ERR_PRINT("Max recursion reached");  				return "[...]";  			} -			stack.push_back(arr.id()); - -			String str("["); -			for (int i = 0; i < arr.size(); i++) { -				if (i) { -					str += ", "; -				} - -				str += arr[i].stringify(stack); -			} -			str += "]"; -			stack.erase(arr.id()); +			String str = stringify_vector(arr, recursion_count);  			return str;  		} break; @@ -1941,8 +1876,6 @@ Variant::operator Basis() const {  		return *_data._basis;  	} else if (type == QUATERNION) {  		return *reinterpret_cast<const Quaternion *>(_data._mem); -	} else if (type == VECTOR3) { -		return Basis(*reinterpret_cast<const Vector3 *>(_data._mem));  	} else if (type == TRANSFORM3D) { // unexposed in Variant::can_convert?  		return _data._transform3d->basis;  	} else { @@ -2824,6 +2757,10 @@ Variant::Variant(const Variant &p_variant) {  }  uint32_t Variant::hash() const { +	return recursive_hash(0); +} + +uint32_t Variant::recursive_hash(int recursion_count) const {  	switch (type) {  		case NIL: {  			return 0; @@ -2832,7 +2769,7 @@ uint32_t Variant::hash() const {  			return _data._bool ? 1 : 0;  		} break;  		case INT: { -			return _data._int; +			return hash_one_uint64((uint64_t)_data._int);  		} break;  		case FLOAT: {  			return hash_djb2_one_float(_data._float); @@ -2847,8 +2784,8 @@ uint32_t Variant::hash() const {  			return hash_djb2_one_float(reinterpret_cast<const Vector2 *>(_data._mem)->y, hash);  		} break;  		case VECTOR2I: { -			uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Vector2i *>(_data._mem)->x); -			return hash_djb2_one_32(reinterpret_cast<const Vector2i *>(_data._mem)->y, hash); +			uint32_t hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector2i *>(_data._mem)->x); +			return hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector2i *>(_data._mem)->y, hash);  		} break;  		case RECT2: {  			uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Rect2 *>(_data._mem)->position.x); @@ -2857,10 +2794,10 @@ uint32_t Variant::hash() const {  			return hash_djb2_one_float(reinterpret_cast<const Rect2 *>(_data._mem)->size.y, hash);  		} break;  		case RECT2I: { -			uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->position.x); -			hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->position.y, hash); -			hash = hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->size.x, hash); -			return hash_djb2_one_32(reinterpret_cast<const Rect2i *>(_data._mem)->size.y, hash); +			uint32_t hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->position.x); +			hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->position.y, hash); +			hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->size.x, hash); +			return hash_djb2_one_32((uint32_t) reinterpret_cast<const Rect2i *>(_data._mem)->size.y, hash);  		} break;  		case TRANSFORM2D: {  			uint32_t hash = 5831; @@ -2878,9 +2815,9 @@ uint32_t Variant::hash() const {  			return hash_djb2_one_float(reinterpret_cast<const Vector3 *>(_data._mem)->z, hash);  		} break;  		case VECTOR3I: { -			uint32_t hash = hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->x); -			hash = hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->y, hash); -			return hash_djb2_one_32(reinterpret_cast<const Vector3i *>(_data._mem)->z, hash); +			uint32_t hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector3i *>(_data._mem)->x); +			hash = hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector3i *>(_data._mem)->y, hash); +			return hash_djb2_one_32((uint32_t) reinterpret_cast<const Vector3i *>(_data._mem)->z, hash);  		} break;  		case PLANE: {  			uint32_t hash = hash_djb2_one_float(reinterpret_cast<const Plane *>(_data._mem)->normal.x); @@ -2951,7 +2888,7 @@ uint32_t Variant::hash() const {  			return reinterpret_cast<const NodePath *>(_data._mem)->hash();  		} break;  		case DICTIONARY: { -			return reinterpret_cast<const Dictionary *>(_data._mem)->hash(); +			return reinterpret_cast<const Dictionary *>(_data._mem)->recursive_hash(recursion_count);  		} break;  		case CALLABLE: { @@ -2965,7 +2902,7 @@ uint32_t Variant::hash() const {  		} break;  		case ARRAY: {  			const Array &arr = *reinterpret_cast<const Array *>(_data._mem); -			return arr.hash(); +			return arr.recursive_hash(recursion_count);  		} break;  		case PACKED_BYTE_ARRAY: { @@ -3139,16 +3076,24 @@ uint32_t Variant::hash() const {                                                                          \  	return true -bool Variant::hash_compare(const Variant &p_variant) const { +bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const {  	if (type != p_variant.type) {  		return false;  	}  	switch (type) { +		case INT: { +			return _data._int == p_variant._data._int; +		} break; +  		case FLOAT: {  			return hash_compare_scalar(_data._float, p_variant._data._float);  		} break; +		case STRING: { +			return *reinterpret_cast<const String *>(_data._mem) == *reinterpret_cast<const String *>(p_variant._data._mem); +		} break; +  		case VECTOR2: {  			const Vector2 *l = reinterpret_cast<const Vector2 *>(_data._mem);  			const Vector2 *r = reinterpret_cast<const Vector2 *>(p_variant._data._mem); @@ -3166,7 +3111,7 @@ bool Variant::hash_compare(const Variant &p_variant) const {  			const Rect2 *r = reinterpret_cast<const Rect2 *>(p_variant._data._mem);  			return (hash_compare_vector2(l->position, r->position)) && -				   (hash_compare_vector2(l->size, r->size)); +					(hash_compare_vector2(l->size, r->size));  		} break;  		case RECT2I: {  			const Rect2i *l = reinterpret_cast<const Rect2i *>(_data._mem); @@ -3206,7 +3151,7 @@ bool Variant::hash_compare(const Variant &p_variant) const {  			const Plane *r = reinterpret_cast<const Plane *>(p_variant._data._mem);  			return (hash_compare_vector3(l->normal, r->normal)) && -				   (hash_compare_scalar(l->d, r->d)); +					(hash_compare_scalar(l->d, r->d));  		} break;  		case AABB: { @@ -3262,14 +3207,19 @@ bool Variant::hash_compare(const Variant &p_variant) const {  			const Array &l = *(reinterpret_cast<const Array *>(_data._mem));  			const Array &r = *(reinterpret_cast<const Array *>(p_variant._data._mem)); -			if (l.size() != r.size()) { +			if (!l.recursive_equal(r, recursion_count + 1)) {  				return false;  			} -			for (int i = 0; i < l.size(); ++i) { -				if (!l[i].hash_compare(r[i])) { -					return false; -				} +			return true; +		} break; + +		case DICTIONARY: { +			const Dictionary &l = *(reinterpret_cast<const Dictionary *>(_data._mem)); +			const Dictionary &r = *(reinterpret_cast<const Dictionary *>(p_variant._data._mem)); + +			if (!l.recursive_equal(r, recursion_count + 1)) { +				return false;  			}  			return true; diff --git a/core/variant/variant.h b/core/variant/variant.h index 9ec131a1b8..230ed33c0c 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -31,6 +31,7 @@  #ifndef VARIANT_H  #define VARIANT_H +#include "core/input/input_enums.h"  #include "core/io/ip_address.h"  #include "core/math/aabb.h"  #include "core/math/basis.h" @@ -43,6 +44,7 @@  #include "core/math/vector3.h"  #include "core/math/vector3i.h"  #include "core/object/object_id.h" +#include "core/os/keyboard.h"  #include "core/string/node_path.h"  #include "core/string/ustring.h"  #include "core/templates/rid.h" @@ -430,6 +432,21 @@ public:  	Variant(const IPAddress &p_address); +#define VARIANT_ENUM_CLASS_CONSTRUCTOR(m_enum) \ +	Variant(const m_enum &p_value) {           \ +		type = INT;                            \ +		_data._int = (int64_t)p_value;         \ +	} + +	// Only enum classes that need to be bound need this to be defined. +	VARIANT_ENUM_CLASS_CONSTRUCTOR(JoyAxis) +	VARIANT_ENUM_CLASS_CONSTRUCTOR(JoyButton) +	VARIANT_ENUM_CLASS_CONSTRUCTOR(Key) +	VARIANT_ENUM_CLASS_CONSTRUCTOR(MIDIMessage) +	VARIANT_ENUM_CLASS_CONSTRUCTOR(MouseButton) + +#undef VARIANT_ENUM_CLASS_CONSTRUCTOR +  	// If this changes the table in variant_op must be updated  	enum Operator {  		//comparison @@ -481,7 +498,8 @@ public:  	static PTROperatorEvaluator get_ptr_operator_evaluator(Operator p_operator, Type p_type_a, Type p_type_b);  	void zero(); -	Variant duplicate(bool deep = false) const; +	Variant duplicate(bool p_deep = false) const; +	Variant recursive_duplicate(bool p_deep, int recursion_count) const;  	static void blend(const Variant &a, const Variant &b, float c, Variant &r_dst);  	static void interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst); @@ -641,6 +659,7 @@ public:  	static UtilityFunctionType get_utility_function_type(const StringName &p_name); +	static MethodInfo get_utility_function_info(const StringName &p_name);  	static int get_utility_function_argument_count(const StringName &p_name);  	static Variant::Type get_utility_function_argument_type(const StringName &p_name, int p_arg);  	static String get_utility_function_argument_name(const StringName &p_name, int p_arg); @@ -658,10 +677,11 @@ public:  	bool operator!=(const Variant &p_variant) const;  	bool operator<(const Variant &p_variant) const;  	uint32_t hash() const; +	uint32_t recursive_hash(int recursion_count) const; -	bool hash_compare(const Variant &p_variant) const; +	bool hash_compare(const Variant &p_variant, int recursion_count = 0) const;  	bool booleanize() const; -	String stringify(List<const void *> &stack) const; +	String stringify(int recursion_count = 0) const;  	String to_json_string() const;  	void static_assign(const Variant &p_variant); diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 382b256c08..82f547e78c 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -752,8 +752,9 @@ struct _VariantCall {  	static PackedInt32Array func_PackedByteArray_decode_s32_array(PackedByteArray *p_instance) {  		uint64_t size = p_instance->size(); -		const uint8_t *r = p_instance->ptr();  		PackedInt32Array dest; +		ERR_FAIL_COND_V_MSG(size < sizeof(int32_t), dest, "Size didn't match array of size int32_t, maybe you are trying to convert to the wrong type?"); +		const uint8_t *r = p_instance->ptr();  		dest.resize(size / sizeof(int32_t));  		memcpy(dest.ptrw(), r, size);  		return dest; @@ -761,8 +762,9 @@ struct _VariantCall {  	static PackedInt64Array func_PackedByteArray_decode_s64_array(PackedByteArray *p_instance) {  		uint64_t size = p_instance->size(); -		const uint8_t *r = p_instance->ptr();  		PackedInt64Array dest; +		ERR_FAIL_COND_V_MSG(size < sizeof(int64_t), dest, "Size didn't match array of size int64_t, maybe you are trying to convert to the wrong type?"); +		const uint8_t *r = p_instance->ptr();  		dest.resize(size / sizeof(int64_t));  		memcpy(dest.ptrw(), r, size);  		return dest; @@ -770,8 +772,9 @@ struct _VariantCall {  	static PackedFloat32Array func_PackedByteArray_decode_float_array(PackedByteArray *p_instance) {  		uint64_t size = p_instance->size(); -		const uint8_t *r = p_instance->ptr();  		PackedFloat32Array dest; +		ERR_FAIL_COND_V_MSG(size < sizeof(float), dest, "Size didn't match array of size float, maybe you are trying to convert to the wrong type?"); +		const uint8_t *r = p_instance->ptr();  		dest.resize(size / sizeof(float));  		memcpy(dest.ptrw(), r, size);  		return dest; @@ -779,8 +782,9 @@ struct _VariantCall {  	static PackedFloat64Array func_PackedByteArray_decode_double_array(PackedByteArray *p_instance) {  		uint64_t size = p_instance->size(); -		const uint8_t *r = p_instance->ptr();  		PackedFloat64Array dest; +		ERR_FAIL_COND_V_MSG(size < sizeof(double), dest, "Size didn't match array of size double, maybe you are trying to convert to the wrong type?"); +		const uint8_t *r = p_instance->ptr();  		dest.resize(size / sizeof(double));  		memcpy(dest.ptrw(), r, size);  		return dest; @@ -1241,8 +1245,8 @@ void Variant::get_constants_for_type(Variant::Type p_type, List<StringName> *p_c  	for (List<StringName>::Element *E = cd.value_ordered.front(); E; E = E->next()) {  		p_constants->push_back(E->get());  #else -	for (Map<StringName, int>::Element *E = cd.value.front(); E; E = E->next()) { -		p_constants->push_back(E->key()); +	for (const KeyValue<StringName, int> &E : cd.value) { +		p_constants->push_back(E.key);  #endif  	} @@ -1250,8 +1254,8 @@ void Variant::get_constants_for_type(Variant::Type p_type, List<StringName> *p_c  	for (List<StringName>::Element *E = cd.variant_value_ordered.front(); E; E = E->next()) {  		p_constants->push_back(E->get());  #else -	for (Map<StringName, Variant>::Element *E = cd.variant_value.front(); E; E = E->next()) { -		p_constants->push_back(E->key()); +	for (const KeyValue<StringName, Variant> &E : cd.variant_value) { +		p_constants->push_back(E.key);  #endif  	}  } @@ -1366,6 +1370,7 @@ static void _register_variant_builtin_methods() {  	bind_method(String, naturalnocasecmp_to, sarray("to"), varray());  	bind_method(String, length, sarray(), varray());  	bind_method(String, substr, sarray("from", "len"), varray(-1)); +	bind_method(String, get_slice, sarray("delimiter", "slice"), varray());  	bind_methodv(String, find, static_cast<int (String::*)(const String &, int) const>(&String::find), sarray("what", "from"), varray(0));  	bind_method(String, count, sarray("what", "from", "to"), varray(0, 0));  	bind_method(String, countn, sarray("what", "from", "to"), varray(0, 0)); @@ -1407,8 +1412,6 @@ static void _register_variant_builtin_methods() {  	bind_method(String, plus_file, sarray("file"), varray());  	bind_method(String, unicode_at, sarray("at"), varray());  	bind_method(String, dedent, sarray(), varray()); -	// FIXME: String needs to be immutable when binding -	//bind_method(String, erase, sarray("position", "chars"), varray());  	bind_method(String, hash, sarray(), varray());  	bind_method(String, md5_text, sarray(), varray());  	bind_method(String, sha1_text, sarray(), varray()); @@ -1417,11 +1420,9 @@ static void _register_variant_builtin_methods() {  	bind_method(String, sha1_buffer, sarray(), varray());  	bind_method(String, sha256_buffer, sarray(), varray());  	bind_method(String, is_empty, sarray(), varray()); -	// FIXME: Static function, not sure how to bind -	//bind_method(String, humanize_size, sarray("size"), varray());  	bind_method(String, is_absolute_path, sarray(), varray()); -	bind_method(String, is_rel_path, sarray(), varray()); +	bind_method(String, is_relative_path, sarray(), varray());  	bind_method(String, simplify_path, sarray(), varray());  	bind_method(String, get_base_dir, sarray(), varray());  	bind_method(String, get_file, sarray(), varray()); @@ -1470,7 +1471,7 @@ static void _register_variant_builtin_methods() {  	bind_method(Vector2, angle, sarray(), varray());  	bind_method(Vector2, angle_to, sarray("to"), varray());  	bind_method(Vector2, angle_to_point, sarray("to"), varray()); -	bind_method(Vector2, direction_to, sarray("b"), varray()); +	bind_method(Vector2, direction_to, sarray("to"), varray());  	bind_method(Vector2, distance_to, sarray("to"), varray());  	bind_method(Vector2, distance_squared_to, sarray("to"), varray());  	bind_method(Vector2, length, sarray(), varray()); @@ -1485,6 +1486,8 @@ static void _register_variant_builtin_methods() {  	bind_method(Vector2, lerp, sarray("to", "weight"), varray());  	bind_method(Vector2, slerp, sarray("to", "weight"), varray());  	bind_method(Vector2, cubic_interpolate, sarray("b", "pre_a", "post_b", "weight"), varray()); +	bind_method(Vector2, max_axis_index, sarray(), varray()); +	bind_method(Vector2, min_axis_index, sarray(), varray());  	bind_method(Vector2, move_toward, sarray("to", "delta"), varray());  	bind_method(Vector2, rotated, sarray("phi"), varray());  	bind_method(Vector2, orthogonal, sarray(), varray()); @@ -1502,15 +1505,20 @@ static void _register_variant_builtin_methods() {  	bind_method(Vector2, clamp, sarray("min", "max"), varray());  	bind_method(Vector2, snapped, sarray("step"), varray()); +	bind_static_method(Vector2, from_angle, sarray("angle"), varray()); +  	/* Vector2i */  	bind_method(Vector2i, aspect, sarray(), varray()); +	bind_method(Vector2i, max_axis_index, sarray(), varray()); +	bind_method(Vector2i, min_axis_index, sarray(), varray());  	bind_method(Vector2i, sign, sarray(), varray());  	bind_method(Vector2i, abs, sarray(), varray());  	bind_method(Vector2i, clamp, sarray("min", "max"), varray());  	/* Rect2 */ +	bind_method(Rect2, get_center, sarray(), varray());  	bind_method(Rect2, get_area, sarray(), varray());  	bind_method(Rect2, has_no_area, sarray(), varray());  	bind_method(Rect2, has_point, sarray("point"), varray()); @@ -1527,6 +1535,7 @@ static void _register_variant_builtin_methods() {  	/* Rect2i */ +	bind_method(Rect2i, get_center, sarray(), varray());  	bind_method(Rect2i, get_area, sarray(), varray());  	bind_method(Rect2i, has_no_area, sarray(), varray());  	bind_method(Rect2i, has_point, sarray("point"), varray()); @@ -1542,13 +1551,13 @@ static void _register_variant_builtin_methods() {  	/* Vector3 */ -	bind_method(Vector3, min_axis, sarray(), varray()); -	bind_method(Vector3, max_axis, sarray(), varray()); +	bind_method(Vector3, min_axis_index, sarray(), varray()); +	bind_method(Vector3, max_axis_index, sarray(), varray());  	bind_method(Vector3, angle_to, sarray("to"), varray());  	bind_method(Vector3, signed_angle_to, sarray("to", "axis"), varray()); -	bind_method(Vector3, direction_to, sarray("b"), varray()); -	bind_method(Vector3, distance_to, sarray("b"), varray()); -	bind_method(Vector3, distance_squared_to, sarray("b"), varray()); +	bind_method(Vector3, direction_to, sarray("to"), varray()); +	bind_method(Vector3, distance_to, sarray("to"), varray()); +	bind_method(Vector3, distance_squared_to, sarray("to"), varray());  	bind_method(Vector3, length, sarray(), varray());  	bind_method(Vector3, length_squared, sarray(), varray());  	bind_method(Vector3, limit_length, sarray("length"), varray(1.0)); @@ -1566,7 +1575,6 @@ static void _register_variant_builtin_methods() {  	bind_method(Vector3, dot, sarray("with"), varray());  	bind_method(Vector3, cross, sarray("with"), varray());  	bind_method(Vector3, outer, sarray("with"), varray()); -	bind_method(Vector3, to_diagonal_matrix, sarray(), varray());  	bind_method(Vector3, abs, sarray(), varray());  	bind_method(Vector3, floor, sarray(), varray());  	bind_method(Vector3, ceil, sarray(), varray()); @@ -1578,11 +1586,13 @@ static void _register_variant_builtin_methods() {  	bind_method(Vector3, bounce, sarray("n"), varray());  	bind_method(Vector3, reflect, sarray("n"), varray());  	bind_method(Vector3, sign, sarray(), varray()); +	bind_method(Vector3, octahedron_encode, sarray(), varray()); +	bind_static_method(Vector3, octahedron_decode, sarray("uv"), varray());  	/* Vector3i */ -	bind_method(Vector3i, min_axis, sarray(), varray()); -	bind_method(Vector3i, max_axis, sarray(), varray()); +	bind_method(Vector3i, min_axis_index, sarray(), varray()); +	bind_method(Vector3i, max_axis_index, sarray(), varray());  	bind_method(Vector3i, sign, sarray(), varray());  	bind_method(Vector3i, abs, sarray(), varray());  	bind_method(Vector3i, clamp, sarray("min", "max"), varray()); @@ -1614,6 +1624,8 @@ static void _register_variant_builtin_methods() {  	bind_method(Quaternion, slerpni, sarray("to", "weight"), varray());  	bind_method(Quaternion, cubic_slerp, sarray("b", "pre_a", "post_b", "weight"), varray());  	bind_method(Quaternion, get_euler, sarray(), varray()); +	bind_method(Quaternion, get_axis, sarray(), varray()); +	bind_method(Quaternion, get_angle, sarray(), varray());  	/* Color */ @@ -1623,17 +1635,15 @@ static void _register_variant_builtin_methods() {  	bind_method(Color, to_argb64, sarray(), varray());  	bind_method(Color, to_abgr64, sarray(), varray());  	bind_method(Color, to_rgba64, sarray(), varray()); +	bind_method(Color, to_html, sarray("with_alpha"), varray(true));  	bind_method(Color, clamp, sarray("min", "max"), varray(Color(0, 0, 0, 0), Color(1, 1, 1, 1)));  	bind_method(Color, inverted, sarray(), varray());  	bind_method(Color, lerp, sarray("to", "weight"), varray());  	bind_method(Color, lightened, sarray("amount"), varray());  	bind_method(Color, darkened, sarray("amount"), varray()); -	bind_method(Color, to_html, sarray("with_alpha"), varray(true));  	bind_method(Color, blend, sarray("over"), varray()); -	// FIXME: Color is immutable, need to probably find a way to do this via constructor -	//ADDFUNC4R(COLOR, COLOR, Color, from_hsv, FLOAT, "h", FLOAT, "s", FLOAT, "v", FLOAT, "a", varray(1.0));  	bind_method(Color, is_equal_approx, sarray("to"), varray());  	bind_static_method(Color, hex, sarray("hex"), varray()); @@ -1645,6 +1655,7 @@ static void _register_variant_builtin_methods() {  	bind_static_method(Color, get_named_color_name, sarray("idx"), varray());  	bind_static_method(Color, get_named_color, sarray("idx"), varray());  	bind_static_method(Color, from_string, sarray("str", "default"), varray()); +	bind_static_method(Color, from_hsv, sarray("h", "s", "v", "alpha"), varray(1.0));  	bind_static_method(Color, from_rgbe9995, sarray("rgbe"), varray());  	/* RID */ @@ -1687,7 +1698,7 @@ static void _register_variant_builtin_methods() {  	bind_method(Signal, get_object_id, sarray(), varray());  	bind_method(Signal, get_name, sarray(), varray()); -	bind_method(Signal, connect, sarray("callable", "binds", "flags"), varray(Array(), 0)); +	bind_method(Signal, connect, sarray("callable", "flags"), varray(0));  	bind_method(Signal, disconnect, sarray("callable"), varray());  	bind_method(Signal, is_connected, sarray("callable"), varray());  	bind_method(Signal, get_connections, sarray(), varray()); @@ -1701,6 +1712,7 @@ static void _register_variant_builtin_methods() {  	bind_method(Transform2D, get_rotation, sarray(), varray());  	bind_method(Transform2D, get_origin, sarray(), varray());  	bind_method(Transform2D, get_scale, sarray(), varray()); +	bind_method(Transform2D, get_skew, sarray(), varray());  	bind_method(Transform2D, orthonormalized, sarray(), varray());  	bind_method(Transform2D, rotated, sarray("phi"), varray());  	bind_method(Transform2D, scaled, sarray("scale"), varray()); @@ -1710,6 +1722,8 @@ static void _register_variant_builtin_methods() {  	bind_method(Transform2D, interpolate_with, sarray("xform", "weight"), varray());  	bind_method(Transform2D, is_equal_approx, sarray("xform"), varray());  	bind_method(Transform2D, set_rotation, sarray("rotation"), varray()); +	bind_method(Transform2D, set_scale, sarray("scale"), varray()); +	bind_method(Transform2D, set_skew, sarray("skew"), varray());  	bind_method(Transform2D, looking_at, sarray("target"), varray(Vector2()));  	/* Basis */ @@ -1721,7 +1735,7 @@ static void _register_variant_builtin_methods() {  	bind_methodv(Basis, rotated, static_cast<Basis (Basis::*)(const Vector3 &, real_t) const>(&Basis::rotated), sarray("axis", "phi"), varray());  	bind_method(Basis, scaled, sarray("scale"), varray());  	bind_method(Basis, get_scale, sarray(), varray()); -	bind_method(Basis, get_euler, sarray(), varray()); +	bind_method(Basis, get_euler, sarray("order"), varray(Basis::EULER_ORDER_YXZ));  	bind_method(Basis, tdotx, sarray("with"), varray());  	bind_method(Basis, tdoty, sarray("with"), varray());  	bind_method(Basis, tdotz, sarray("with"), varray()); @@ -1730,12 +1744,15 @@ static void _register_variant_builtin_methods() {  	bind_method(Basis, is_equal_approx, sarray("b"), varray());  	bind_method(Basis, get_rotation_quaternion, sarray(), varray());  	bind_static_method(Basis, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0))); +	bind_static_method(Basis, from_scale, sarray("scale"), varray()); +	bind_static_method(Basis, from_euler, sarray("euler", "order"), varray(Basis::EULER_ORDER_YXZ));  	/* AABB */  	bind_method(AABB, abs, sarray(), varray()); -	bind_method(AABB, get_area, sarray(), varray()); -	bind_method(AABB, has_no_area, sarray(), varray()); +	bind_method(AABB, get_center, sarray(), varray()); +	bind_method(AABB, get_volume, sarray(), varray()); +	bind_method(AABB, has_no_volume, sarray(), varray());  	bind_method(AABB, has_no_surface, sarray(), varray());  	bind_method(AABB, has_point, sarray("point"), varray());  	bind_method(AABB, is_equal_approx, sarray("aabb"), varray()); @@ -1795,7 +1812,7 @@ static void _register_variant_builtin_methods() {  	bind_method(Array, append_array, sarray("array"), varray());  	bind_method(Array, resize, sarray("size"), varray());  	bind_method(Array, insert, sarray("position", "value"), varray()); -	bind_method(Array, remove, sarray("position"), varray()); +	bind_method(Array, remove_at, sarray("position"), varray());  	bind_method(Array, fill, sarray("value"), varray());  	bind_method(Array, erase, sarray("value"), varray());  	bind_method(Array, front, sarray(), varray()); @@ -1829,14 +1846,15 @@ static void _register_variant_builtin_methods() {  	bind_method(PackedByteArray, push_back, sarray("value"), varray());  	bind_method(PackedByteArray, append, sarray("value"), varray());  	bind_method(PackedByteArray, append_array, sarray("array"), varray()); -	bind_method(PackedByteArray, remove, sarray("index"), varray()); +	bind_method(PackedByteArray, remove_at, sarray("index"), varray());  	bind_method(PackedByteArray, insert, sarray("at_index", "value"), varray());  	bind_method(PackedByteArray, fill, sarray("value"), varray());  	bind_method(PackedByteArray, resize, sarray("new_size"), varray());  	bind_method(PackedByteArray, has, sarray("value"), varray());  	bind_method(PackedByteArray, reverse, sarray(), varray()); -	bind_method(PackedByteArray, subarray, sarray("from", "to"), varray()); +	bind_method(PackedByteArray, slice, sarray("begin", "end"), varray());  	bind_method(PackedByteArray, sort, sarray(), varray()); +	bind_method(PackedByteArray, bsearch, sarray("value", "before"), varray(true));  	bind_method(PackedByteArray, duplicate, sarray(), varray());  	bind_function(PackedByteArray, get_string_from_ascii, _VariantCall::func_PackedByteArray_get_string_from_ascii, sarray(), varray()); @@ -1889,15 +1907,16 @@ static void _register_variant_builtin_methods() {  	bind_method(PackedInt32Array, push_back, sarray("value"), varray());  	bind_method(PackedInt32Array, append, sarray("value"), varray());  	bind_method(PackedInt32Array, append_array, sarray("array"), varray()); -	bind_method(PackedInt32Array, remove, sarray("index"), varray()); +	bind_method(PackedInt32Array, remove_at, sarray("index"), varray());  	bind_method(PackedInt32Array, insert, sarray("at_index", "value"), varray());  	bind_method(PackedInt32Array, fill, sarray("value"), varray());  	bind_method(PackedInt32Array, resize, sarray("new_size"), varray());  	bind_method(PackedInt32Array, has, sarray("value"), varray());  	bind_method(PackedInt32Array, reverse, sarray(), varray()); -	bind_method(PackedInt32Array, subarray, sarray("from", "to"), varray()); +	bind_method(PackedInt32Array, slice, sarray("begin", "end"), varray());  	bind_method(PackedInt32Array, to_byte_array, sarray(), varray());  	bind_method(PackedInt32Array, sort, sarray(), varray()); +	bind_method(PackedInt32Array, bsearch, sarray("value", "before"), varray(true));  	bind_method(PackedInt32Array, duplicate, sarray(), varray());  	/* Int64 Array */ @@ -1908,15 +1927,16 @@ static void _register_variant_builtin_methods() {  	bind_method(PackedInt64Array, push_back, sarray("value"), varray());  	bind_method(PackedInt64Array, append, sarray("value"), varray());  	bind_method(PackedInt64Array, append_array, sarray("array"), varray()); -	bind_method(PackedInt64Array, remove, sarray("index"), varray()); +	bind_method(PackedInt64Array, remove_at, sarray("index"), varray());  	bind_method(PackedInt64Array, insert, sarray("at_index", "value"), varray());  	bind_method(PackedInt64Array, fill, sarray("value"), varray());  	bind_method(PackedInt64Array, resize, sarray("new_size"), varray());  	bind_method(PackedInt64Array, has, sarray("value"), varray());  	bind_method(PackedInt64Array, reverse, sarray(), varray()); -	bind_method(PackedInt64Array, subarray, sarray("from", "to"), varray()); +	bind_method(PackedInt64Array, slice, sarray("begin", "end"), varray());  	bind_method(PackedInt64Array, to_byte_array, sarray(), varray());  	bind_method(PackedInt64Array, sort, sarray(), varray()); +	bind_method(PackedInt64Array, bsearch, sarray("value", "before"), varray(true));  	bind_method(PackedInt64Array, duplicate, sarray(), varray());  	/* Float32 Array */ @@ -1927,15 +1947,16 @@ static void _register_variant_builtin_methods() {  	bind_method(PackedFloat32Array, push_back, sarray("value"), varray());  	bind_method(PackedFloat32Array, append, sarray("value"), varray());  	bind_method(PackedFloat32Array, append_array, sarray("array"), varray()); -	bind_method(PackedFloat32Array, remove, sarray("index"), varray()); +	bind_method(PackedFloat32Array, remove_at, sarray("index"), varray());  	bind_method(PackedFloat32Array, insert, sarray("at_index", "value"), varray());  	bind_method(PackedFloat32Array, fill, sarray("value"), varray());  	bind_method(PackedFloat32Array, resize, sarray("new_size"), varray());  	bind_method(PackedFloat32Array, has, sarray("value"), varray());  	bind_method(PackedFloat32Array, reverse, sarray(), varray()); -	bind_method(PackedFloat32Array, subarray, sarray("from", "to"), varray()); +	bind_method(PackedFloat32Array, slice, sarray("begin", "end"), varray());  	bind_method(PackedFloat32Array, to_byte_array, sarray(), varray());  	bind_method(PackedFloat32Array, sort, sarray(), varray()); +	bind_method(PackedFloat32Array, bsearch, sarray("value", "before"), varray(true));  	bind_method(PackedFloat32Array, duplicate, sarray(), varray());  	/* Float64 Array */ @@ -1946,15 +1967,16 @@ static void _register_variant_builtin_methods() {  	bind_method(PackedFloat64Array, push_back, sarray("value"), varray());  	bind_method(PackedFloat64Array, append, sarray("value"), varray());  	bind_method(PackedFloat64Array, append_array, sarray("array"), varray()); -	bind_method(PackedFloat64Array, remove, sarray("index"), varray()); +	bind_method(PackedFloat64Array, remove_at, sarray("index"), varray());  	bind_method(PackedFloat64Array, insert, sarray("at_index", "value"), varray());  	bind_method(PackedFloat64Array, fill, sarray("value"), varray());  	bind_method(PackedFloat64Array, resize, sarray("new_size"), varray());  	bind_method(PackedFloat64Array, has, sarray("value"), varray());  	bind_method(PackedFloat64Array, reverse, sarray(), varray()); -	bind_method(PackedFloat64Array, subarray, sarray("from", "to"), varray()); +	bind_method(PackedFloat64Array, slice, sarray("begin", "end"), varray());  	bind_method(PackedFloat64Array, to_byte_array, sarray(), varray());  	bind_method(PackedFloat64Array, sort, sarray(), varray()); +	bind_method(PackedFloat64Array, bsearch, sarray("value", "before"), varray(true));  	bind_method(PackedFloat64Array, duplicate, sarray(), varray());  	/* String Array */ @@ -1965,15 +1987,16 @@ static void _register_variant_builtin_methods() {  	bind_method(PackedStringArray, push_back, sarray("value"), varray());  	bind_method(PackedStringArray, append, sarray("value"), varray());  	bind_method(PackedStringArray, append_array, sarray("array"), varray()); -	bind_method(PackedStringArray, remove, sarray("index"), varray()); +	bind_method(PackedStringArray, remove_at, sarray("index"), varray());  	bind_method(PackedStringArray, insert, sarray("at_index", "value"), varray());  	bind_method(PackedStringArray, fill, sarray("value"), varray());  	bind_method(PackedStringArray, resize, sarray("new_size"), varray());  	bind_method(PackedStringArray, has, sarray("value"), varray());  	bind_method(PackedStringArray, reverse, sarray(), varray()); -	bind_method(PackedStringArray, subarray, sarray("from", "to"), varray()); +	bind_method(PackedStringArray, slice, sarray("begin", "end"), varray());  	bind_method(PackedStringArray, to_byte_array, sarray(), varray());  	bind_method(PackedStringArray, sort, sarray(), varray()); +	bind_method(PackedStringArray, bsearch, sarray("value", "before"), varray(true));  	bind_method(PackedStringArray, duplicate, sarray(), varray());  	/* Vector2 Array */ @@ -1984,15 +2007,16 @@ static void _register_variant_builtin_methods() {  	bind_method(PackedVector2Array, push_back, sarray("value"), varray());  	bind_method(PackedVector2Array, append, sarray("value"), varray());  	bind_method(PackedVector2Array, append_array, sarray("array"), varray()); -	bind_method(PackedVector2Array, remove, sarray("index"), varray()); +	bind_method(PackedVector2Array, remove_at, sarray("index"), varray());  	bind_method(PackedVector2Array, insert, sarray("at_index", "value"), varray());  	bind_method(PackedVector2Array, fill, sarray("value"), varray());  	bind_method(PackedVector2Array, resize, sarray("new_size"), varray());  	bind_method(PackedVector2Array, has, sarray("value"), varray());  	bind_method(PackedVector2Array, reverse, sarray(), varray()); -	bind_method(PackedVector2Array, subarray, sarray("from", "to"), varray()); +	bind_method(PackedVector2Array, slice, sarray("begin", "end"), varray());  	bind_method(PackedVector2Array, to_byte_array, sarray(), varray());  	bind_method(PackedVector2Array, sort, sarray(), varray()); +	bind_method(PackedVector2Array, bsearch, sarray("value", "before"), varray(true));  	bind_method(PackedVector2Array, duplicate, sarray(), varray());  	/* Vector3 Array */ @@ -2003,15 +2027,16 @@ static void _register_variant_builtin_methods() {  	bind_method(PackedVector3Array, push_back, sarray("value"), varray());  	bind_method(PackedVector3Array, append, sarray("value"), varray());  	bind_method(PackedVector3Array, append_array, sarray("array"), varray()); -	bind_method(PackedVector3Array, remove, sarray("index"), varray()); +	bind_method(PackedVector3Array, remove_at, sarray("index"), varray());  	bind_method(PackedVector3Array, insert, sarray("at_index", "value"), varray());  	bind_method(PackedVector3Array, fill, sarray("value"), varray());  	bind_method(PackedVector3Array, resize, sarray("new_size"), varray());  	bind_method(PackedVector3Array, has, sarray("value"), varray());  	bind_method(PackedVector3Array, reverse, sarray(), varray()); -	bind_method(PackedVector3Array, subarray, sarray("from", "to"), varray()); +	bind_method(PackedVector3Array, slice, sarray("begin", "end"), varray());  	bind_method(PackedVector3Array, to_byte_array, sarray(), varray());  	bind_method(PackedVector3Array, sort, sarray(), varray()); +	bind_method(PackedVector3Array, bsearch, sarray("value", "before"), varray(true));  	bind_method(PackedVector3Array, duplicate, sarray(), varray());  	/* Color Array */ @@ -2022,15 +2047,16 @@ static void _register_variant_builtin_methods() {  	bind_method(PackedColorArray, push_back, sarray("value"), varray());  	bind_method(PackedColorArray, append, sarray("value"), varray());  	bind_method(PackedColorArray, append_array, sarray("array"), varray()); -	bind_method(PackedColorArray, remove, sarray("index"), varray()); +	bind_method(PackedColorArray, remove_at, sarray("index"), varray());  	bind_method(PackedColorArray, insert, sarray("at_index", "value"), varray());  	bind_method(PackedColorArray, fill, sarray("value"), varray());  	bind_method(PackedColorArray, resize, sarray("new_size"), varray());  	bind_method(PackedColorArray, has, sarray("value"), varray());  	bind_method(PackedColorArray, reverse, sarray(), varray()); -	bind_method(PackedColorArray, subarray, sarray("from", "to"), varray()); +	bind_method(PackedColorArray, slice, sarray("begin", "end"), varray());  	bind_method(PackedColorArray, to_byte_array, sarray(), varray());  	bind_method(PackedColorArray, sort, sarray(), varray()); +	bind_method(PackedColorArray, bsearch, sarray("value", "before"), varray(true));  	bind_method(PackedColorArray, duplicate, sarray(), varray());  	/* Register constants */ @@ -2088,6 +2114,13 @@ static void _register_variant_builtin_methods() {  	_VariantCall::add_variant_constant(Variant::VECTOR2I, "UP", Vector2i(0, -1));  	_VariantCall::add_variant_constant(Variant::VECTOR2I, "DOWN", Vector2i(0, 1)); +	_VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_XYZ", Basis::EULER_ORDER_XYZ); +	_VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_XZY", Basis::EULER_ORDER_XZY); +	_VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_YXZ", Basis::EULER_ORDER_YXZ); +	_VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_YZX", Basis::EULER_ORDER_YZX); +	_VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_ZXY", Basis::EULER_ORDER_ZXY); +	_VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_ZYX", Basis::EULER_ORDER_ZYX); +  	_VariantCall::add_variant_constant(Variant::TRANSFORM2D, "IDENTITY", Transform2D());  	_VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_X", Transform2D(-1, 0, 0, 1, 0, 0));  	_VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_Y", Transform2D(1, 0, 0, -1, 0, 0)); diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp index a1a2bec369..5c14f30180 100644 --- a/core/variant/variant_construct.cpp +++ b/core/variant/variant_construct.cpp @@ -114,22 +114,24 @@ void Variant::_register_variant_constructors() {  	add_constructor<VariantConstructNoArgs<Transform2D>>(sarray());  	add_constructor<VariantConstructor<Transform2D, Transform2D>>(sarray("from"));  	add_constructor<VariantConstructor<Transform2D, float, Vector2>>(sarray("rotation", "position")); +	add_constructor<VariantConstructor<Transform2D, float, Size2, float, Vector2>>(sarray("rotation", "scale", "skew", "position"));  	add_constructor<VariantConstructor<Transform2D, Vector2, Vector2, Vector2>>(sarray("x_axis", "y_axis", "origin"));  	add_constructor<VariantConstructNoArgs<Plane>>(sarray());  	add_constructor<VariantConstructor<Plane, Plane>>(sarray("from")); +	add_constructor<VariantConstructor<Plane, Vector3>>(sarray("normal"));  	add_constructor<VariantConstructor<Plane, Vector3, double>>(sarray("normal", "d")); -	add_constructor<VariantConstructor<Plane, Vector3, Vector3>>(sarray("point", "normal")); +	add_constructor<VariantConstructor<Plane, Vector3, Vector3>>(sarray("normal", "point"));  	add_constructor<VariantConstructor<Plane, Vector3, Vector3, Vector3>>(sarray("point1", "point2", "point3"));  	add_constructor<VariantConstructor<Plane, double, double, double, double>>(sarray("a", "b", "c", "d"));  	add_constructor<VariantConstructNoArgs<Quaternion>>(sarray());  	add_constructor<VariantConstructor<Quaternion, Quaternion>>(sarray("from"));  	add_constructor<VariantConstructor<Quaternion, Basis>>(sarray("from")); -	add_constructor<VariantConstructor<Quaternion, Vector3>>(sarray("euler"));  	add_constructor<VariantConstructor<Quaternion, Vector3, double>>(sarray("axis", "angle"));  	add_constructor<VariantConstructor<Quaternion, Vector3, Vector3>>(sarray("arc_from", "arc_to"));  	add_constructor<VariantConstructor<Quaternion, double, double, double, double>>(sarray("x", "y", "z", "w")); +	add_constructor<VariantConstructor<Quaternion, Vector3>>(sarray("euler_yxz"));  	add_constructor<VariantConstructNoArgs<::AABB>>(sarray());  	add_constructor<VariantConstructor<::AABB, ::AABB>>(sarray("from")); @@ -138,7 +140,6 @@ void Variant::_register_variant_constructors() {  	add_constructor<VariantConstructNoArgs<Basis>>(sarray());  	add_constructor<VariantConstructor<Basis, Basis>>(sarray("from"));  	add_constructor<VariantConstructor<Basis, Quaternion>>(sarray("from")); -	add_constructor<VariantConstructor<Basis, Vector3>>(sarray("euler"));  	add_constructor<VariantConstructor<Basis, Vector3, double>>(sarray("axis", "phi"));  	add_constructor<VariantConstructor<Basis, Vector3, Vector3, Vector3>>(sarray("x_axis", "y_axis", "z_axis")); diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 40c8a1bfde..d24f3e90f5 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -753,8 +753,20 @@ VARIANT_ACCESSOR_NUMBER(uint32_t)  VARIANT_ACCESSOR_NUMBER(int64_t)  VARIANT_ACCESSOR_NUMBER(uint64_t)  VARIANT_ACCESSOR_NUMBER(char32_t) + +// Bind enums to allow using them as return types.  VARIANT_ACCESSOR_NUMBER(Error)  VARIANT_ACCESSOR_NUMBER(Side) +VARIANT_ACCESSOR_NUMBER(Vector2::Axis) +VARIANT_ACCESSOR_NUMBER(Vector2i::Axis) +VARIANT_ACCESSOR_NUMBER(Vector3::Axis) +VARIANT_ACCESSOR_NUMBER(Vector3i::Axis) + +template <> +struct VariantInternalAccessor<Basis::EulerOrder> { +	static _FORCE_INLINE_ Basis::EulerOrder get(const Variant *v) { return Basis::EulerOrder(*VariantInternal::get_int(v)); } +	static _FORCE_INLINE_ void set(Variant *v, Basis::EulerOrder p_value) { *VariantInternal::get_int(v) = p_value; } +};  template <>  struct VariantInternalAccessor<ObjectID> { @@ -1014,6 +1026,10 @@ INITIALIZER_INT(int64_t)  INITIALIZER_INT(char32_t)  INITIALIZER_INT(Error)  INITIALIZER_INT(ObjectID) +INITIALIZER_INT(Vector2::Axis) +INITIALIZER_INT(Vector2i::Axis) +INITIALIZER_INT(Vector3::Axis) +INITIALIZER_INT(Vector3i::Axis)  template <>  struct VariantInitializer<double> { @@ -1301,12 +1317,12 @@ struct VariantZeroAssigner<Signal> {  template <>  struct VariantZeroAssigner<Dictionary> { -	static _FORCE_INLINE_ void zero(Variant *v) {} +	static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_dictionary(v) = Dictionary(); }  };  template <>  struct VariantZeroAssigner<Array> { -	static _FORCE_INLINE_ void zero(Variant *v) {} +	static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_array(v) = Array(); }  };  template <> diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp index a245aff35b..b85ece338c 100644 --- a/core/variant/variant_op.cpp +++ b/core/variant/variant_op.cpp @@ -314,6 +314,74 @@ void Variant::_register_variant_operators() {  	register_op<OperatorEvaluatorEqual<PackedVector3Array, PackedVector3Array>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::PACKED_VECTOR3_ARRAY);  	register_op<OperatorEvaluatorEqual<PackedColorArray, PackedColorArray>>(Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::PACKED_COLOR_ARRAY); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::BOOL, Variant::NIL>>(Variant::OP_EQUAL, Variant::BOOL, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::INT, Variant::NIL>>(Variant::OP_EQUAL, Variant::INT, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::FLOAT, Variant::NIL>>(Variant::OP_EQUAL, Variant::FLOAT, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::STRING, Variant::NIL>>(Variant::OP_EQUAL, Variant::STRING, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR2, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR2, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR2I, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR2I, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::RECT2, Variant::NIL>>(Variant::OP_EQUAL, Variant::RECT2, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::RECT2I, Variant::NIL>>(Variant::OP_EQUAL, Variant::RECT2I, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR3, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR3, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR3I, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR3I, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::TRANSFORM2D, Variant::NIL>>(Variant::OP_EQUAL, Variant::TRANSFORM2D, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PLANE, Variant::NIL>>(Variant::OP_EQUAL, Variant::PLANE, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::QUATERNION, Variant::NIL>>(Variant::OP_EQUAL, Variant::QUATERNION, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::AABB, Variant::NIL>>(Variant::OP_EQUAL, Variant::AABB, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::BASIS, Variant::NIL>>(Variant::OP_EQUAL, Variant::BASIS, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::TRANSFORM3D, Variant::NIL>>(Variant::OP_EQUAL, Variant::TRANSFORM3D, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::COLOR, Variant::NIL>>(Variant::OP_EQUAL, Variant::COLOR, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::STRING_NAME, Variant::NIL>>(Variant::OP_EQUAL, Variant::STRING_NAME, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NODE_PATH, Variant::NIL>>(Variant::OP_EQUAL, Variant::NODE_PATH, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::RID, Variant::NIL>>(Variant::OP_EQUAL, Variant::RID, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::CALLABLE, Variant::NIL>>(Variant::OP_EQUAL, Variant::CALLABLE, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::SIGNAL, Variant::NIL>>(Variant::OP_EQUAL, Variant::SIGNAL, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::DICTIONARY, Variant::NIL>>(Variant::OP_EQUAL, Variant::DICTIONARY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL); + +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::BOOL>>(Variant::OP_EQUAL, Variant::NIL, Variant::BOOL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::INT>>(Variant::OP_EQUAL, Variant::NIL, Variant::INT); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::FLOAT>>(Variant::OP_EQUAL, Variant::NIL, Variant::FLOAT); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::STRING>>(Variant::OP_EQUAL, Variant::NIL, Variant::STRING); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR2>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR2); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR2I>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR2I); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::RECT2>>(Variant::OP_EQUAL, Variant::NIL, Variant::RECT2); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::RECT2I>>(Variant::OP_EQUAL, Variant::NIL, Variant::RECT2I); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3I>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3I); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM2D>>(Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM2D); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PLANE>>(Variant::OP_EQUAL, Variant::NIL, Variant::PLANE); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::QUATERNION>>(Variant::OP_EQUAL, Variant::NIL, Variant::QUATERNION); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::AABB>>(Variant::OP_EQUAL, Variant::NIL, Variant::AABB); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::BASIS>>(Variant::OP_EQUAL, Variant::NIL, Variant::BASIS); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM3D>>(Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM3D); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::COLOR>>(Variant::OP_EQUAL, Variant::NIL, Variant::COLOR); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::STRING_NAME>>(Variant::OP_EQUAL, Variant::NIL, Variant::STRING_NAME); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::NODE_PATH>>(Variant::OP_EQUAL, Variant::NIL, Variant::NODE_PATH); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::RID>>(Variant::OP_EQUAL, Variant::NIL, Variant::RID); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::CALLABLE>>(Variant::OP_EQUAL, Variant::NIL, Variant::CALLABLE); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::SIGNAL>>(Variant::OP_EQUAL, Variant::NIL, Variant::SIGNAL); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::DICTIONARY>>(Variant::OP_EQUAL, Variant::NIL, Variant::DICTIONARY); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::ARRAY); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_BYTE_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_BYTE_ARRAY); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_INT32_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_INT32_ARRAY); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_INT64_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_INT64_ARRAY); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_FLOAT32_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_FLOAT32_ARRAY); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_FLOAT64_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_FLOAT64_ARRAY); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_STRING_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_STRING_ARRAY); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY); +	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY); +  	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NIL);  	register_op<OperatorEvaluatorNotEqual<bool, bool>>(Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::BOOL);  	register_op<OperatorEvaluatorNotEqual<int64_t, int64_t>>(Variant::OP_NOT_EQUAL, Variant::INT, Variant::INT); @@ -360,6 +428,74 @@ void Variant::_register_variant_operators() {  	register_op<OperatorEvaluatorNotEqual<PackedVector3Array, PackedVector3Array>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::PACKED_VECTOR3_ARRAY);  	register_op<OperatorEvaluatorNotEqual<PackedColorArray, PackedColorArray>>(Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::PACKED_COLOR_ARRAY); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::INT, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::INT, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::FLOAT, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::FLOAT, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::STRING, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::STRING, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR2, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR2, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR2I, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR2I, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::RECT2, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::RECT2, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::RECT2I, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::RECT2I, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR3, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR3I, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3I, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::TRANSFORM2D, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM2D, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PLANE, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PLANE, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::QUATERNION, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::QUATERNION, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::AABB, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::AABB, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::BASIS, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::BASIS, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::TRANSFORM3D, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM3D, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::COLOR, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::COLOR, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::STRING_NAME, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::STRING_NAME, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NODE_PATH, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::NODE_PATH, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::RID, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::RID, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::CALLABLE, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::CALLABLE, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::SIGNAL, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::SIGNAL, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::DICTIONARY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::DICTIONARY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_BYTE_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_INT32_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_INT64_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_STRING_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL); + +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BOOL>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BOOL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::INT>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::INT); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::FLOAT>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::FLOAT); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::STRING>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::STRING); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR2>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR2); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR2I>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR2I); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2I>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2I); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3I>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3I); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM2D>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM2D); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PLANE>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PLANE); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::QUATERNION>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::QUATERNION); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::AABB>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::AABB); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BASIS>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BASIS); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM3D>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM3D); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::COLOR>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::COLOR); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::STRING_NAME>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::STRING_NAME); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NODE_PATH>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NODE_PATH); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RID>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RID); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::CALLABLE>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::CALLABLE); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::SIGNAL>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::SIGNAL); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::DICTIONARY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::DICTIONARY); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::ARRAY); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_BYTE_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_BYTE_ARRAY); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_INT32_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_INT32_ARRAY); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_INT64_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_INT64_ARRAY); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_FLOAT32_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_FLOAT32_ARRAY); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_FLOAT64_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_FLOAT64_ARRAY); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_STRING_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_STRING_ARRAY); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY); +	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY); +  	register_op<OperatorEvaluatorLess<bool, bool>>(Variant::OP_LESS, Variant::BOOL, Variant::BOOL);  	register_op<OperatorEvaluatorLess<int64_t, int64_t>>(Variant::OP_LESS, Variant::INT, Variant::INT);  	register_op<OperatorEvaluatorLess<int64_t, double>>(Variant::OP_LESS, Variant::INT, Variant::FLOAT); @@ -505,7 +641,10 @@ void Variant::_register_variant_operators() {  	register_op<OperatorEvaluatorNotFloat>(Variant::OP_NOT, Variant::FLOAT, Variant::NIL);  	register_op<OperatorEvaluatorNotObject>(Variant::OP_NOT, Variant::OBJECT, Variant::NIL); -	register_op<OperatorEvaluatorInStringFind>(Variant::OP_IN, Variant::STRING, Variant::STRING); +	register_op<OperatorEvaluatorInStringFind<String>>(Variant::OP_IN, Variant::STRING, Variant::STRING); +	register_op<OperatorEvaluatorInStringFind<StringName>>(Variant::OP_IN, Variant::STRING_NAME, Variant::STRING); +	register_op<OperatorEvaluatorInStringNameFind<String>>(Variant::OP_IN, Variant::STRING, Variant::STRING_NAME); +	register_op<OperatorEvaluatorInStringNameFind<StringName>>(Variant::OP_IN, Variant::STRING_NAME, Variant::STRING_NAME);  	register_op<OperatorEvaluatorInDictionaryHasNil>(Variant::OP_IN, Variant::NIL, Variant::DICTIONARY);  	register_op<OperatorEvaluatorInDictionaryHas<bool>>(Variant::OP_IN, Variant::BOOL, Variant::DICTIONARY); diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h index 3c9f849a4f..353524469a 100644 --- a/core/variant/variant_op.h +++ b/core/variant/variant_op.h @@ -1213,22 +1213,44 @@ public:  //// +template <class Left>  class OperatorEvaluatorInStringFind {  public:  	static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { -		const String &str_a = *VariantGetInternalPtr<String>::get_ptr(&p_left); +		const Left &str_a = *VariantGetInternalPtr<Left>::get_ptr(&p_left);  		const String &str_b = *VariantGetInternalPtr<String>::get_ptr(&p_right);  		*r_ret = str_b.find(str_a) != -1;  		r_valid = true;  	}  	static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { -		const String &str_a = *VariantGetInternalPtr<String>::get_ptr(left); +		const Left &str_a = *VariantGetInternalPtr<Left>::get_ptr(left);  		const String &str_b = *VariantGetInternalPtr<String>::get_ptr(right);  		*VariantGetInternalPtr<bool>::get_ptr(r_ret) = str_b.find(str_a) != -1;  	}  	static void ptr_evaluate(const void *left, const void *right, void *r_ret) { -		PtrToArg<bool>::encode(PtrToArg<String>::convert(right).find(PtrToArg<String>::convert(left)) != -1, r_ret); +		PtrToArg<bool>::encode(PtrToArg<String>::convert(right).find(PtrToArg<Left>::convert(left)) != -1, r_ret); +	} +	static Variant::Type get_return_type() { return Variant::BOOL; } +}; + +template <class Left> +class OperatorEvaluatorInStringNameFind { +public: +	static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { +		const Left &str_a = *VariantGetInternalPtr<Left>::get_ptr(&p_left); +		const String str_b = VariantGetInternalPtr<StringName>::get_ptr(&p_right)->operator String(); + +		*r_ret = str_b.find(str_a) != -1; +		r_valid = true; +	} +	static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { +		const Left &str_a = *VariantGetInternalPtr<Left>::get_ptr(left); +		const String str_b = VariantGetInternalPtr<StringName>::get_ptr(right)->operator String(); +		*VariantGetInternalPtr<bool>::get_ptr(r_ret) = str_b.find(str_a) != -1; +	} +	static void ptr_evaluate(const void *left, const void *right, void *r_ret) { +		PtrToArg<bool>::encode(PtrToArg<StringName>::convert(right).operator String().find(PtrToArg<Left>::convert(left)) != -1, r_ret);  	}  	static Variant::Type get_return_type() { return Variant::BOOL; }  }; diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index a214a238a6..47561f4621 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -90,6 +90,17 @@ const char *VariantParser::tk_name[TK_MAX] = {  	"ERROR"  }; +static double stor_fix(const String &p_str) { +	if (p_str == "inf") { +		return INFINITY; +	} else if (p_str == "inf_neg") { +		return -INFINITY; +	} else if (p_str == "nan") { +		return NAN; +	} +	return -1; +} +  Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, String &r_err_str) {  	bool string_name = false; @@ -469,8 +480,19 @@ Error VariantParser::_parse_construct(Stream *p_stream, Vector<T> &r_construct,  		if (first && token.type == TK_PARENTHESIS_CLOSE) {  			break;  		} else if (token.type != TK_NUMBER) { -			r_err_str = "Expected float in constructor"; -			return ERR_PARSE_ERROR; +			bool valid = false; +			if (token.type == TK_IDENTIFIER) { +				double real = stor_fix(token.value); +				if (real != -1) { +					token.type = TK_NUMBER; +					token.value = real; +					valid = true; +				} +			} +			if (!valid) { +				r_err_str = "Expected float in constructor"; +				return ERR_PARSE_ERROR; +			}  		}  		r_construct.push_back(token.value); @@ -507,6 +529,8 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,  			value = Variant();  		} else if (id == "inf") {  			value = INFINITY; +		} else if (id == "inf_neg") { +			value = -INFINITY;  		} else if (id == "nan") {  			value = NAN;  		} else if (id == "Vector2") { @@ -1401,17 +1425,25 @@ Error VariantParser::parse(Stream *p_stream, Variant &r_ret, String &r_err_str,  //////////////////////////////////////////////////////////////////////////////////  ////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// -static String rtosfix(double p_value) { +static String rtos_fix(double p_value) {  	if (p_value == 0.0) {  		return "0"; //avoid negative zero (-0) being written, which may annoy git, svn, etc. for changes when they don't exist. +	} else if (isnan(p_value)) { +		return "nan"; +	} else if (isinf(p_value)) { +		if (p_value > 0) { +			return "inf"; +		} else { +			return "inf_neg"; +		}  	} else {  		return rtoss(p_value);  	}  } -Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud) { +Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int recursion_count) {  	switch (p_variant.get_type()) {  		case Variant::NIL: {  			p_store_string_func(p_store_string_ud, "null"); @@ -1423,8 +1455,8 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  			p_store_string_func(p_store_string_ud, itos(p_variant.operator int64_t()));  		} break;  		case Variant::FLOAT: { -			String s = rtosfix(p_variant.operator double()); -			if (s != "inf" && s != "nan") { +			String s = rtos_fix(p_variant.operator double()); +			if (s != "inf" && s != "inf_neg" && s != "nan") {  				if (s.find(".") == -1 && s.find("e") == -1) {  					s += ".0";  				} @@ -1439,7 +1471,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  		} break;  		case Variant::VECTOR2: {  			Vector2 v = p_variant; -			p_store_string_func(p_store_string_ud, "Vector2(" + rtosfix(v.x) + ", " + rtosfix(v.y) + ")"); +			p_store_string_func(p_store_string_ud, "Vector2(" + rtos_fix(v.x) + ", " + rtos_fix(v.y) + ")");  		} break;  		case Variant::VECTOR2I: {  			Vector2i v = p_variant; @@ -1447,7 +1479,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  		} break;  		case Variant::RECT2: {  			Rect2 aabb = p_variant; -			p_store_string_func(p_store_string_ud, "Rect2(" + rtosfix(aabb.position.x) + ", " + rtosfix(aabb.position.y) + ", " + rtosfix(aabb.size.x) + ", " + rtosfix(aabb.size.y) + ")"); +			p_store_string_func(p_store_string_ud, "Rect2(" + rtos_fix(aabb.position.x) + ", " + rtos_fix(aabb.position.y) + ", " + rtos_fix(aabb.size.x) + ", " + rtos_fix(aabb.size.y) + ")");  		} break;  		case Variant::RECT2I: { @@ -1457,7 +1489,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  		} break;  		case Variant::VECTOR3: {  			Vector3 v = p_variant; -			p_store_string_func(p_store_string_ud, "Vector3(" + rtosfix(v.x) + ", " + rtosfix(v.y) + ", " + rtosfix(v.z) + ")"); +			p_store_string_func(p_store_string_ud, "Vector3(" + rtos_fix(v.x) + ", " + rtos_fix(v.y) + ", " + rtos_fix(v.z) + ")");  		} break;  		case Variant::VECTOR3I: {  			Vector3i v = p_variant; @@ -1465,17 +1497,17 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  		} break;  		case Variant::PLANE: {  			Plane p = p_variant; -			p_store_string_func(p_store_string_ud, "Plane(" + rtosfix(p.normal.x) + ", " + rtosfix(p.normal.y) + ", " + rtosfix(p.normal.z) + ", " + rtosfix(p.d) + ")"); +			p_store_string_func(p_store_string_ud, "Plane(" + rtos_fix(p.normal.x) + ", " + rtos_fix(p.normal.y) + ", " + rtos_fix(p.normal.z) + ", " + rtos_fix(p.d) + ")");  		} break;  		case Variant::AABB: {  			AABB aabb = p_variant; -			p_store_string_func(p_store_string_ud, "AABB(" + rtosfix(aabb.position.x) + ", " + rtosfix(aabb.position.y) + ", " + rtosfix(aabb.position.z) + ", " + rtosfix(aabb.size.x) + ", " + rtosfix(aabb.size.y) + ", " + rtosfix(aabb.size.z) + ")"); +			p_store_string_func(p_store_string_ud, "AABB(" + rtos_fix(aabb.position.x) + ", " + rtos_fix(aabb.position.y) + ", " + rtos_fix(aabb.position.z) + ", " + rtos_fix(aabb.size.x) + ", " + rtos_fix(aabb.size.y) + ", " + rtos_fix(aabb.size.z) + ")");  		} break;  		case Variant::QUATERNION: {  			Quaternion quaternion = p_variant; -			p_store_string_func(p_store_string_ud, "Quaternion(" + rtosfix(quaternion.x) + ", " + rtosfix(quaternion.y) + ", " + rtosfix(quaternion.z) + ", " + rtosfix(quaternion.w) + ")"); +			p_store_string_func(p_store_string_ud, "Quaternion(" + rtos_fix(quaternion.x) + ", " + rtos_fix(quaternion.y) + ", " + rtos_fix(quaternion.z) + ", " + rtos_fix(quaternion.w) + ")");  		} break;  		case Variant::TRANSFORM2D: { @@ -1486,7 +1518,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  					if (i != 0 || j != 0) {  						s += ", ";  					} -					s += rtosfix(m3.elements[i][j]); +					s += rtos_fix(m3.elements[i][j]);  				}  			} @@ -1501,7 +1533,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  					if (i != 0 || j != 0) {  						s += ", ";  					} -					s += rtosfix(m3.elements[i][j]); +					s += rtos_fix(m3.elements[i][j]);  				}  			} @@ -1517,11 +1549,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  					if (i != 0 || j != 0) {  						s += ", ";  					} -					s += rtosfix(m3.elements[i][j]); +					s += rtos_fix(m3.elements[i][j]);  				}  			} -			s = s + ", " + rtosfix(t.origin.x) + ", " + rtosfix(t.origin.y) + ", " + rtosfix(t.origin.z); +			s = s + ", " + rtos_fix(t.origin.x) + ", " + rtos_fix(t.origin.y) + ", " + rtos_fix(t.origin.z);  			p_store_string_func(p_store_string_ud, s + ")");  		} break; @@ -1529,7 +1561,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  		// misc types  		case Variant::COLOR: {  			Color c = p_variant; -			p_store_string_func(p_store_string_ud, "Color(" + rtosfix(c.r) + ", " + rtosfix(c.g) + ", " + rtosfix(c.b) + ", " + rtosfix(c.a) + ")"); +			p_store_string_func(p_store_string_ud, "Color(" + rtos_fix(c.r) + ", " + rtos_fix(c.g) + ", " + rtos_fix(c.b) + ", " + rtos_fix(c.a) + ")");  		} break;  		case Variant::STRING_NAME: { @@ -1566,14 +1598,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  				}  				//try path because it's a file -				if (res_text == String() && res->get_path().is_resource_file()) { +				if (res_text.is_empty() && res->get_path().is_resource_file()) {  					//external resource  					String path = res->get_path();  					res_text = "Resource(\"" + path + "\")";  				}  				//could come up with some sort of text -				if (res_text != String()) { +				if (!res_text.is_empty()) {  					p_store_string_func(p_store_string_ud, res_text);  					break;  				} @@ -1607,41 +1639,56 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  		case Variant::DICTIONARY: {  			Dictionary dict = p_variant; - -			List<Variant> keys; -			dict.get_key_list(&keys); -			keys.sort(); - -			p_store_string_func(p_store_string_ud, "{\n"); -			for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { -				/* -				if (!_check_type(dict[E])) -					continue; -				*/ -				write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); -				p_store_string_func(p_store_string_ud, ": "); -				write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); -				if (E->next()) { -					p_store_string_func(p_store_string_ud, ",\n"); -				} else { -					p_store_string_func(p_store_string_ud, "\n"); +			if (recursion_count > MAX_RECURSION) { +				ERR_PRINT("Max recursion reached"); +				p_store_string_func(p_store_string_ud, "{}"); +			} else { +				recursion_count++; + +				List<Variant> keys; +				dict.get_key_list(&keys); +				keys.sort(); + +				p_store_string_func(p_store_string_ud, "{\n"); +				for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { +					/* +					if (!_check_type(dict[E->get()])) +						continue; +					*/ +					write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count); +					p_store_string_func(p_store_string_ud, ": "); +					write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count); +					if (E->next()) { +						p_store_string_func(p_store_string_ud, ",\n"); +					} else { +						p_store_string_func(p_store_string_ud, "\n"); +					}  				} -			} -			p_store_string_func(p_store_string_ud, "}"); +				p_store_string_func(p_store_string_ud, "}"); +			}  		} break; +  		case Variant::ARRAY: { -			p_store_string_func(p_store_string_ud, "["); -			Array array = p_variant; -			int len = array.size(); -			for (int i = 0; i < len; i++) { -				if (i > 0) { -					p_store_string_func(p_store_string_ud, ", "); +			if (recursion_count > MAX_RECURSION) { +				ERR_PRINT("Max recursion reached"); +				p_store_string_func(p_store_string_ud, "[]"); +			} else { +				recursion_count++; + +				p_store_string_func(p_store_string_ud, "["); +				Array array = p_variant; +				int len = array.size(); +				for (int i = 0; i < len; i++) { +					if (i > 0) { +						p_store_string_func(p_store_string_ud, ", "); +					} +					write(array[i], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count);  				} -				write(array[i], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); + +				p_store_string_func(p_store_string_ud, "]");  			} -			p_store_string_func(p_store_string_ud, "]");  		} break; @@ -1707,7 +1754,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  				if (i > 0) {  					p_store_string_func(p_store_string_ud, ", ");  				} -				p_store_string_func(p_store_string_ud, rtosfix(ptr[i])); +				p_store_string_func(p_store_string_ud, rtos_fix(ptr[i]));  			}  			p_store_string_func(p_store_string_ud, ")"); @@ -1723,7 +1770,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  				if (i > 0) {  					p_store_string_func(p_store_string_ud, ", ");  				} -				p_store_string_func(p_store_string_ud, rtosfix(ptr[i])); +				p_store_string_func(p_store_string_ud, rtos_fix(ptr[i]));  			}  			p_store_string_func(p_store_string_ud, ")"); @@ -1759,7 +1806,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  				if (i > 0) {  					p_store_string_func(p_store_string_ud, ", ");  				} -				p_store_string_func(p_store_string_ud, rtosfix(ptr[i].x) + ", " + rtosfix(ptr[i].y)); +				p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].x) + ", " + rtos_fix(ptr[i].y));  			}  			p_store_string_func(p_store_string_ud, ")"); @@ -1775,7 +1822,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  				if (i > 0) {  					p_store_string_func(p_store_string_ud, ", ");  				} -				p_store_string_func(p_store_string_ud, rtosfix(ptr[i].x) + ", " + rtosfix(ptr[i].y) + ", " + rtosfix(ptr[i].z)); +				p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].x) + ", " + rtos_fix(ptr[i].y) + ", " + rtos_fix(ptr[i].z));  			}  			p_store_string_func(p_store_string_ud, ")"); @@ -1792,7 +1839,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str  					p_store_string_func(p_store_string_ud, ", ");  				} -				p_store_string_func(p_store_string_ud, rtosfix(ptr[i].r) + ", " + rtosfix(ptr[i].g) + ", " + rtosfix(ptr[i].b) + ", " + rtosfix(ptr[i].a)); +				p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].r) + ", " + rtos_fix(ptr[i].g) + ", " + rtos_fix(ptr[i].b) + ", " + rtos_fix(ptr[i].a));  			}  			p_store_string_func(p_store_string_ud, ")"); diff --git a/core/variant/variant_parser.h b/core/variant/variant_parser.h index 1ba26db6ed..2e4baa6fff 100644 --- a/core/variant/variant_parser.h +++ b/core/variant/variant_parser.h @@ -140,7 +140,7 @@ public:  	typedef Error (*StoreStringFunc)(void *ud, const String &p_string);  	typedef String (*EncodeResourceFunc)(void *ud, const RES &p_resource); -	static Error write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud); +	static Error write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int recursion_count = 0);  	static Error write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func = nullptr, void *p_encode_res_ud = nullptr);  }; diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp index ae3c7685fd..b6ad2d870e 100644 --- a/core/variant/variant_setget.cpp +++ b/core/variant/variant_setget.cpp @@ -239,7 +239,8 @@ void Variant::set_named(const StringName &p_member, const Variant &p_value, bool  			*v = p_value;  			r_valid = true;  		} else { -			r_valid = false; +			VariantGetInternalPtr<Dictionary>::get_ptr(this)->operator[](p_member) = p_value; +			r_valid = true;  		}  	} else { @@ -661,6 +662,91 @@ struct VariantIndexedSetGet_Array {  	static uint64_t get_indexed_size(const Variant *base) { return 0; }  }; +struct VariantIndexedSetGet_String { +	static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { +		int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length(); +		if (index < 0) { +			index += length; +		} +		if (index < 0 || index >= length) { +			*oob = true; +			return; +		} +		char32_t result = (*VariantGetInternalPtr<String>::get_ptr(base))[index]; +		*value = String(&result, 1); +		*oob = false; +	} +	static void ptr_get(const void *base, int64_t index, void *member) { +		/* avoid ptrconvert for performance*/ +		const String &v = *reinterpret_cast<const String *>(base); +		if (index < 0) { +			index += v.length(); +		} +		OOB_TEST(index, v.length()); +		char32_t c = v[index]; +		PtrToArg<String>::encode(String(&c, 1), member); +	} +	static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { +		if (value->get_type() != Variant::STRING) { +			*oob = false; +			*valid = false; +			return; +		} +		int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length(); +		if (index < 0) { +			index += length; +		} +		if (index < 0 || index >= length) { +			*oob = true; +			*valid = false; +			return; +		} +		String *b = VariantGetInternalPtr<String>::get_ptr(base); +		const String *v = VariantInternal::get_string(value); +		if (v->length() == 0) { +			b->remove_at(index); +		} else { +			b->set(index, v->get(0)); +		} +		*oob = false; +		*valid = true; +	} +	static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { +		int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length(); +		if (index < 0) { +			index += length; +		} +		if (index < 0 || index >= length) { +			*oob = true; +			return; +		} +		String *b = VariantGetInternalPtr<String>::get_ptr(base); +		const String *v = VariantInternal::get_string(value); +		if (v->length() == 0) { +			b->remove_at(index); +		} else { +			b->set(index, v->get(0)); +		} +		*oob = false; +	} +	static void ptr_set(void *base, int64_t index, const void *member) { +		/* avoid ptrconvert for performance*/ +		String &v = *reinterpret_cast<String *>(base); +		if (index < 0) { +			index += v.length(); +		} +		OOB_TEST(index, v.length()); +		const String &m = *reinterpret_cast<const String *>(member); +		if (unlikely(m.length() == 0)) { +			v.remove_at(index); +		} else { +			v.set(index, m.unicode_at(0)); +		} +	} +	static Variant::Type get_index_type() { return Variant::STRING; } +	static uint64_t get_indexed_size(const Variant *base) { return VariantInternal::get_string(base)->length(); } +}; +  #define INDEXED_SETGET_STRUCT_DICT(m_base_type)                                                                                     \  	struct VariantIndexedSetGet_##m_base_type {                                                                                     \  		static void get(const Variant *base, int64_t index, Variant *value, bool *oob) {                                            \ @@ -758,6 +844,7 @@ static void register_indexed_member(Variant::Type p_type) {  void register_indexed_setters_getters() {  #define REGISTER_INDEXED_MEMBER(m_base_type) register_indexed_member<VariantIndexedSetGet_##m_base_type>(GetTypeInfo<m_base_type>::VARIANT_TYPE) +	REGISTER_INDEXED_MEMBER(String);  	REGISTER_INDEXED_MEMBER(Vector2);  	REGISTER_INDEXED_MEMBER(Vector2i);  	REGISTER_INDEXED_MEMBER(Vector3); @@ -1737,11 +1824,15 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {  	return Variant();  } -Variant Variant::duplicate(bool deep) const { +Variant Variant::duplicate(bool p_deep) const { +	return recursive_duplicate(p_deep, 0); +} + +Variant Variant::recursive_duplicate(bool p_deep, int recursion_count) const {  	switch (type) {  		case OBJECT: {  			/*  breaks stuff :( -			if (deep && !_get_obj().ref.is_null()) { +			if (p_deep && !_get_obj().ref.is_null()) {  				Ref<Resource> resource = _get_obj().ref;  				if (resource.is_valid()) {  					return resource->duplicate(true); @@ -1751,9 +1842,9 @@ Variant Variant::duplicate(bool deep) const {  			return *this;  		} break;  		case DICTIONARY: -			return operator Dictionary().duplicate(deep); +			return operator Dictionary().recursive_duplicate(p_deep, recursion_count);  		case ARRAY: -			return operator Array().duplicate(deep); +			return operator Array().recursive_duplicate(p_deep, recursion_count);  		case PACKED_BYTE_ARRAY:  			return operator Vector<uint8_t>().duplicate();  		case PACKED_INT32_ARRAY: diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index f5cb2d40d6..554b2f1c25 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -151,10 +151,10 @@ struct VariantUtilityFunctions {  		r_error.error = Callable::CallError::CALL_OK;  		switch (x.get_type()) {  			case Variant::INT: { -				return SGN(VariantInternalAccessor<int64_t>::get(&x)); +				return SIGN(VariantInternalAccessor<int64_t>::get(&x));  			} break;  			case Variant::FLOAT: { -				return SGN(VariantInternalAccessor<double>::get(&x)); +				return SIGN(VariantInternalAccessor<double>::get(&x));  			} break;  			case Variant::VECTOR2: {  				return VariantInternalAccessor<Vector2>::get(&x).sign(); @@ -176,11 +176,11 @@ struct VariantUtilityFunctions {  	}  	static inline double signf(double x) { -		return SGN(x); +		return SIGN(x);  	}  	static inline int64_t signi(int64_t x) { -		return SGN(x); +		return SIGN(x);  	}  	static inline double pow(double x, double y) { @@ -267,14 +267,6 @@ struct VariantUtilityFunctions {  		return Math::db2linear(db);  	} -	static inline Vector2 polar2cartesian(double r, double th) { -		return Vector2(r * Math::cos(th), r * Math::sin(th)); -	} - -	static inline Vector2 cartesian2polar(double x, double y) { -		return Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); -	} -  	static inline int64_t wrapi(int64_t value, int64_t min, int64_t max) {  		return Math::wrapi(value, min, max);  	} @@ -283,6 +275,10 @@ struct VariantUtilityFunctions {  		return Math::wrapf(value, min, max);  	} +	static inline double pingpong(double value, double length) { +		return Math::pingpong(value, length); +	} +  	static inline Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {  		if (p_argcount < 2) {  			r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; @@ -407,6 +403,10 @@ struct VariantUtilityFunctions {  		return Math::randf();  	} +	static inline double randfn(double mean, double deviation) { +		return Math::randfn(mean, deviation); +	} +  	static inline int64_t randi_range(int64_t from, int64_t to) {  		return Math::random((int32_t)from, (int32_t)to);  	} @@ -495,10 +495,6 @@ struct VariantUtilityFunctions {  	}  	static inline void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { -		if (p_arg_count < 1) { -			r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; -			r_error.argument = 1; -		}  		String str;  		for (int i = 0; i < p_arg_count; i++) {  			String os = p_args[i]->operator String(); @@ -514,11 +510,29 @@ struct VariantUtilityFunctions {  		r_error.error = Callable::CallError::CALL_OK;  	} -	static inline void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { -		if (p_arg_count < 1) { -			r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; -			r_error.argument = 1; +	static inline void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { +		if (OS::get_singleton()->is_stdout_verbose()) { +			String str; +			for (int i = 0; i < p_arg_count; i++) { +				String os = p_args[i]->operator String(); + +				if (i == 0) { +					str = os; +				} else { +					str += os; +				} +			} + +			// No need to use `print_verbose()` as this call already only happens +			// when verbose mode is enabled. This avoids performing string argument concatenation +			// when not needed. +			print_line(str);  		} + +		r_error.error = Callable::CallError::CALL_OK; +	} + +	static inline void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {  		String str;  		for (int i = 0; i < p_arg_count; i++) {  			String os = p_args[i]->operator String(); @@ -535,10 +549,6 @@ struct VariantUtilityFunctions {  	}  	static inline void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { -		if (p_arg_count < 1) { -			r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; -			r_error.argument = 1; -		}  		String str;  		for (int i = 0; i < p_arg_count; i++) {  			if (i) { @@ -552,10 +562,6 @@ struct VariantUtilityFunctions {  	}  	static inline void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { -		if (p_arg_count < 1) { -			r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; -			r_error.argument = 1; -		}  		String str;  		for (int i = 0; i < p_arg_count; i++) {  			if (i) { @@ -569,10 +575,6 @@ struct VariantUtilityFunctions {  	}  	static inline void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { -		if (p_arg_count < 1) { -			r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; -			r_error.argument = 1; -		}  		String str;  		for (int i = 0; i < p_arg_count; i++) {  			String os = p_args[i]->operator String(); @@ -1214,9 +1216,6 @@ void Variant::_register_variant_utility_functions() {  	FUNCBINDR(linear2db, sarray("lin"), Variant::UTILITY_FUNC_TYPE_MATH);  	FUNCBINDR(db2linear, sarray("db"), Variant::UTILITY_FUNC_TYPE_MATH); -	FUNCBINDR(polar2cartesian, sarray("r", "th"), Variant::UTILITY_FUNC_TYPE_MATH); -	FUNCBINDR(cartesian2polar, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH); -  	FUNCBINDR(wrapi, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH);  	FUNCBINDR(wrapf, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH); @@ -1235,6 +1234,7 @@ void Variant::_register_variant_utility_functions() {  	FUNCBINDR(clampf, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH);  	FUNCBINDR(nearest_po2, sarray("value"), Variant::UTILITY_FUNC_TYPE_MATH); +	FUNCBINDR(pingpong, sarray("value", "length"), Variant::UTILITY_FUNC_TYPE_MATH);  	// Random @@ -1243,6 +1243,7 @@ void Variant::_register_variant_utility_functions() {  	FUNCBINDR(randf, sarray(), Variant::UTILITY_FUNC_TYPE_RANDOM);  	FUNCBINDR(randi_range, sarray("from", "to"), Variant::UTILITY_FUNC_TYPE_RANDOM);  	FUNCBINDR(randf_range, sarray("from", "to"), Variant::UTILITY_FUNC_TYPE_RANDOM); +	FUNCBINDR(randfn, sarray("mean", "deviation"), Variant::UTILITY_FUNC_TYPE_RANDOM);  	FUNCBIND(seed, sarray("base"), Variant::UTILITY_FUNC_TYPE_RANDOM);  	FUNCBINDR(rand_from_seed, sarray("seed"), Variant::UTILITY_FUNC_TYPE_RANDOM); @@ -1257,6 +1258,7 @@ void Variant::_register_variant_utility_functions() {  	FUNCBINDVARARGV(printt, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);  	FUNCBINDVARARGV(prints, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);  	FUNCBINDVARARGV(printraw, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); +	FUNCBINDVARARGV(print_verbose, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);  	FUNCBINDVARARGV(push_error, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL);  	FUNCBINDVARARGV(push_warning, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); @@ -1341,6 +1343,28 @@ Variant::UtilityFunctionType Variant::get_utility_function_type(const StringName  	return bfi->type;  } +MethodInfo Variant::get_utility_function_info(const StringName &p_name) { +	MethodInfo info; +	const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); +	if (bfi) { +		info.name = p_name; +		if (bfi->returns_value && bfi->return_type == Variant::NIL) { +			info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; +		} +		info.return_val.type = bfi->return_type; +		if (bfi->is_vararg) { +			info.flags |= METHOD_FLAG_VARARG; +		} +		for (int i = 0; i < bfi->argnames.size(); ++i) { +			PropertyInfo arg; +			arg.type = bfi->get_arg_type(i); +			arg.name = bfi->argnames[i]; +			info.arguments.push_back(arg); +		} +	} +	return info; +} +  int Variant::get_utility_function_argument_count(const StringName &p_name) {  	const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name);  	if (!bfi) {  |