From bb5e51330a33b069fe069ca0da4703199eff11c5 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 11:20:19 -0700 Subject: [PATCH 001/134] Initial commit --- .gitignore | 3 + .vscode/c_cpp_properties.json | 22 ++ .vscode/cmake-kits.json | 15 + .vscode/extensions.json | 9 + .vscode/launch.json | 50 +++ .vscode/settings.json | 40 ++ .vscode/tasks.json | 102 ++++++ CMakeLists.txt | 64 ++++ pico_sdk_import.cmake | 121 +++++++ switch-pico.cpp | 76 ++++ switch_pro_descriptors.h | 511 ++++++++++++++++++++++++++ switch_pro_driver.cpp | 663 ++++++++++++++++++++++++++++++++++ switch_pro_driver.h | 50 +++ tusb_config.h | 38 ++ 14 files changed, 1764 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/cmake-kits.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 CMakeLists.txt create mode 100644 pico_sdk_import.cmake create mode 100644 switch-pico.cpp create mode 100644 switch_pro_descriptors.h create mode 100644 switch_pro_driver.cpp create mode 100644 switch_pro_driver.h create mode 100644 tusb_config.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ed84ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +GP2040-CE +build +!.vscode/* diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..0a6cd5a --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/.vscode/cmake-kits.json b/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..cd4187a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": true, + "cmake.automaticReconfigure": true, + "cmake.configureOnOpen": true, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": false, + "raspberry-pi-pico.useCmakeTools": true, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7ff57c6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,64 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(switch-pico C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(switch-pico + switch-pico.cpp + switch_pro_driver.cpp +) + +pico_set_program_name(switch-pico "switch-pico") +pico_set_program_version(switch-pico "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(switch-pico 0) +pico_enable_stdio_usb(switch-pico 0) + +# Add the standard library to the build +target_link_libraries(switch-pico + pico_stdlib + tinyusb_device + tinyusb_board + hardware_uart + pico_rand +) + +# Add the standard include files to the build +target_include_directories(switch-pico PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(switch-pico) diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/switch-pico.cpp b/switch-pico.cpp new file mode 100644 index 0000000..88f9fe6 --- /dev/null +++ b/switch-pico.cpp @@ -0,0 +1,76 @@ +#include +#include "bsp/board.h" +#include "hardware/uart.h" +#include "pico/stdlib.h" +#include "tusb.h" +#include "switch_pro_driver.h" + +// UART1 is reserved for external input frames from the host PC. +#define UART_ID uart1 +#define BAUD_RATE 115200 +#define UART_TX_PIN 4 +#define UART_RX_PIN 5 + +static void init_uart_input() { + uart_init(UART_ID, BAUD_RATE); + gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); + gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART); + uart_set_format(UART_ID, 8, 1, UART_PARITY_NONE); +} + +static SwitchInputState neutral_input() { + SwitchInputState state{}; + state.lx = SWITCH_PRO_JOYSTICK_MID; + state.ly = SWITCH_PRO_JOYSTICK_MID; + state.rx = SWITCH_PRO_JOYSTICK_MID; + state.ry = SWITCH_PRO_JOYSTICK_MID; + return state; +} + +// Consume UART bytes and forward complete frames to the Switch Pro driver. +static void poll_uart_frames() { + static uint8_t buffer[8]; + static uint8_t index = 0; + static absolute_time_t last_byte_time = {0}; + static bool has_last_byte = false; + + while (uart_is_readable(UART_ID)) { + uint8_t byte = uart_getc(UART_ID); + + uint64_t now = to_ms_since_boot(get_absolute_time()); + if (has_last_byte && (now - to_ms_since_boot(last_byte_time)) > 20) { + index = 0; // stale data, restart frame + } + last_byte_time = get_absolute_time(); + has_last_byte = true; + + if (index == 0) { + if (byte != 0xAA) { + continue; // wait for start-of-frame marker + } + } + + buffer[index++] = byte; + if (index >= sizeof(buffer)) { + switch_pro_apply_uart_packet(buffer, sizeof(buffer)); + index = 0; + } + } +} + +int main() { + board_init(); + stdio_init_all(); + + init_uart_input(); + + tusb_init(); + switch_pro_init(); + switch_pro_set_input(neutral_input()); + + while (true) { + tud_task(); // USB device tasks + poll_uart_frames(); // Pull controller state from UART1 + switch_pro_task(); // Push state to the Switch host + } +} diff --git a/switch_pro_descriptors.h b/switch_pro_descriptors.h new file mode 100644 index 0000000..be1a667 --- /dev/null +++ b/switch_pro_descriptors.h @@ -0,0 +1,511 @@ +/* + * Switch Pro controller descriptor and data definitions. + * Copied from the GP2040-CE project to mirror the behaviour of its + * SwitchProDriver without pulling in the rest of the codebase. + */ + +#pragma once + +#include + +#define SWITCH_PRO_ENDPOINT_SIZE 64 + +// HAT report (4 bits) +#define SWITCH_PRO_HAT_UP 0x00 +#define SWITCH_PRO_HAT_UPRIGHT 0x01 +#define SWITCH_PRO_HAT_RIGHT 0x02 +#define SWITCH_PRO_HAT_DOWNRIGHT 0x03 +#define SWITCH_PRO_HAT_DOWN 0x04 +#define SWITCH_PRO_HAT_DOWNLEFT 0x05 +#define SWITCH_PRO_HAT_LEFT 0x06 +#define SWITCH_PRO_HAT_UPLEFT 0x07 +#define SWITCH_PRO_HAT_NOTHING 0x08 + +#define SWITCH_PRO_MASK_ZR (1U << 7) +#define SWITCH_PRO_MASK_R (1U << 6) +#define SWITCH_PRO_MASK_A (1U << 3) +#define SWITCH_PRO_MASK_B (1U << 2) +#define SWITCH_PRO_MASK_X (1U << 1) +#define SWITCH_PRO_MASK_Y 1U + +#define SWITCH_PRO_MASK_CAPTURE (1U << 5) +#define SWITCH_PRO_MASK_HOME (1U << 4) +#define SWITCH_PRO_MASK_L3 (1U << 3) +#define SWITCH_PRO_MASK_R3 (1U << 2) +#define SWITCH_PRO_MASK_PLUS (1U << 1) +#define SWITCH_PRO_MASK_MINUS 1U + +#define SWITCH_PRO_MASK_ZL (1U << 7) +#define SWITCH_PRO_MASK_L (1U << 6) + +#define SWITCH_PRO_JOYSTICK_MIN 0x0000 +#define SWITCH_PRO_JOYSTICK_MID 0x7FFF +#define SWITCH_PRO_JOYSTICK_MAX 0xFFFF + +typedef enum { + REPORT_OUTPUT_00 = 0x00, + REPORT_FEATURE = 0x01, + REPORT_OUTPUT_10 = 0x10, + REPORT_OUTPUT_21 = 0x21, + REPORT_OUTPUT_30 = 0x30, + REPORT_CONFIGURATION = 0x80, + REPORT_USB_INPUT_81 = 0x81, +} SwitchReportID; + +typedef enum { + IDENTIFY = 0x01, + HANDSHAKE, + BAUD_RATE, + DISABLE_USB_TIMEOUT, + ENABLE_USB_TIMEOUT +} SwitchOutputSubtypes; + +typedef enum { + GET_CONTROLLER_STATE = 0x00, + BLUETOOTH_PAIR_REQUEST = 0x01, + REQUEST_DEVICE_INFO = 0x02, + SET_MODE = 0x03, + TRIGGER_BUTTONS = 0x04, + SET_SHIPMENT = 0x08, + SPI_READ = 0x10, + SET_NFC_IR_CONFIG = 0x21, + SET_NFC_IR_STATE = 0x22, + SET_PLAYER_LIGHTS = 0x30, + GET_PLAYER_LIGHTS = 0x31, + COMMAND_UNKNOWN_33 = 0x33, + SET_HOME_LIGHT = 0x38, + TOGGLE_IMU = 0x40, + IMU_SENSITIVITY = 0x41, + READ_IMU = 0x43, + ENABLE_VIBRATION = 0x48, + GET_VOLTAGE = 0x50, +} SwitchCommands; + +typedef struct { + uint8_t data[3]; + + void setX(uint16_t x) { + data[0] = x & 0xFF; + data[1] = (data[1] & 0xF0) | ((x >> 8) & 0x0F); + } + + void setY(uint16_t y) { + data[1] = (data[1] & 0x0F) | ((y & 0x0F) << 4); + data[2] = (y >> 4) & 0xFF; + } + + uint16_t getX() { + return static_cast(data[0]) | ((data[1] & 0x0F) << 8); + } + + uint16_t getY() { + return static_cast((data[1] >> 4)) | (data[2] << 4); + } +} SwitchAnalog; + +// left and right calibration are stored differently for some reason, so two structs +typedef struct { + uint8_t data[9]; + + void getMin(uint16_t& x, uint16_t& y) const { packCalib(6, x, y); } + void getCenter(uint16_t& x, uint16_t& y) const { packCalib(3, x, y); } + void getMax(uint16_t& x, uint16_t& y) const { packCalib(0, x, y); } + + void getRealMin(uint16_t& x, uint16_t& y) const { + uint16_t minX, minY; + uint16_t cenX, cenY; + + getMin(minX, minY); + getCenter(cenX, cenY); + + x = cenX - minX; + y = cenY - minY; + } + + void getRealMax(uint16_t& x, uint16_t& y) const { + uint16_t maxX, maxY; + uint16_t cenX, cenY; + + getMax(maxX, maxY); + getCenter(cenX, cenY); + + x = cenX + maxX; + y = cenY + maxY; + } + + void packCalib(uint8_t offset, uint16_t& x, uint16_t& y) const { + x = static_cast(data[offset]) | ((data[offset + 1] & 0x0F) << 8); + y = static_cast(data[offset + 2] << 4) | (data[offset + 1] >> 4); + } +} SwitchLeftCalibration; + +typedef struct { + uint8_t data[9]; + + void getMin(uint16_t& x, uint16_t& y) const { packCalib(3, x, y); } + void getCenter(uint16_t& x, uint16_t& y) const { packCalib(0, x, y); } + void getMax(uint16_t& x, uint16_t& y) const { packCalib(6, x, y); } + + void getRealMin(uint16_t& x, uint16_t& y) const { + uint16_t minX, minY; + uint16_t cenX, cenY; + + getMin(minX, minY); + getCenter(cenX, cenY); + + x = cenX - minX; + y = cenY - minY; + } + + void getRealMax(uint16_t& x, uint16_t& y) const { + uint16_t maxX, maxY; + uint16_t cenX, cenY; + + getMax(maxX, maxY); + getCenter(cenX, cenY); + + x = cenX + maxX; + y = cenY + maxY; + } + + void packCalib(uint8_t offset, uint16_t& x, uint16_t& y) const { + x = static_cast(data[offset]) | ((data[offset + 1] & 0x0F) << 8); + y = static_cast(data[offset + 2] << 4) | (data[offset + 1] >> 4); + } +} SwitchRightCalibration; + +typedef struct +{ + uint8_t red; + uint8_t green; + uint8_t blue; +} SwitchColorDefinition; + +typedef struct __attribute((packed, aligned(1))) +{ + uint8_t serialNumber[16]; + + uint8_t unknown00[2]; + + uint8_t deviceType; + + uint8_t unknown01; // usually 0xA0 + + uint8_t unknown02[7]; + + uint8_t colorInfo; // 0 = default colors + + uint8_t unknown03[4]; + + uint8_t motionCalibration[24]; + + uint8_t unknown04[5]; + + SwitchLeftCalibration leftStickCalibration; + + SwitchRightCalibration rightStickCalibration; + + uint8_t unknown08; + + SwitchColorDefinition bodyColor; + + SwitchColorDefinition buttonColor; + + SwitchColorDefinition leftGripColor; + + SwitchColorDefinition rightGripColor; + + uint8_t unknown06[37]; + + uint8_t motionHorizontalOffsets[6]; + + uint8_t stickParams1[17]; + + uint8_t stickParams2[17]; + + uint8_t unknown07[0xE57]; +} SwitchFactoryConfig; + +typedef struct __attribute((packed, aligned(1))) +{ + uint8_t unknown00[16]; + + uint8_t leftCalibrationMagic[2]; + SwitchLeftCalibration leftCalibration; + + uint8_t rightCalibrationMagic[2]; + SwitchRightCalibration rightCalibration; + + uint8_t motionCalibrationMagic[2]; + uint8_t motionCalibration[24]; +} SwitchUserCalibration; + +typedef struct __attribute((packed, aligned(1))) +{ + uint8_t connectionInfo : 4; + uint8_t batteryLevel : 4; + + // byte 00 + uint8_t buttonY : 1; + uint8_t buttonX : 1; + uint8_t buttonB : 1; + uint8_t buttonA : 1; + uint8_t buttonRightSR : 1; + uint8_t buttonRightSL : 1; + uint8_t buttonR : 1; + uint8_t buttonZR : 1; + + // byte 01 + uint8_t buttonMinus : 1; + uint8_t buttonPlus : 1; + uint8_t buttonThumbR : 1; + uint8_t buttonThumbL : 1; + uint8_t buttonHome : 1; + uint8_t buttonCapture : 1; + uint8_t dummy : 1; + uint8_t chargingGrip : 1; + + // byte 02 + uint8_t dpadDown : 1; + uint8_t dpadUp : 1; + uint8_t dpadRight : 1; + uint8_t dpadLeft : 1; + uint8_t buttonLeftSL : 1; + uint8_t buttonLeftSR : 1; + uint8_t buttonL : 1; + uint8_t buttonZL : 1; + + SwitchAnalog leftStick; + SwitchAnalog rightStick; +} SwitchInputReport; + +typedef struct __attribute((packed, aligned(1))) +{ + uint8_t reportID; + uint8_t timestamp; + SwitchInputReport inputs; + uint8_t rumbleReport; + uint8_t imuData[36]; + uint8_t padding[15]; +} SwitchProReport; + +typedef enum { + SWITCH_TYPE_LEFT_JOYCON = 0x01, + SWITCH_TYPE_RIGHT_JOYCON, + SWITCH_TYPE_PRO_CONTROLLER, + SWITCH_TYPE_FAMICOM_LEFT_JOYCON = 0x07, + SWITCH_TYPE_FAMICOM_RIGHT_JOYCON = 0x08, + SWITCH_TYPE_NES_LEFT_JOYCON = 0x09, + SWITCH_TYPE_NES_RIGHT_JOYCON = 0x0A, + SWITCH_TYPE_SNES = 0x0B, + SWITCH_TYPE_N64 = 0x0C, +} SwitchControllerType; + +typedef struct { + uint8_t majorVersion; + uint8_t minorVersion; + uint8_t controllerType; + uint8_t unknown00; + uint8_t macAddress[6]; + uint8_t unknown01; + uint8_t storedColors; +} SwitchDeviceInfo; + +typedef struct +{ + uint16_t buttons; + uint8_t hat; + uint8_t lx; + uint8_t ly; + uint8_t rx; + uint8_t ry; +} SwitchProOutReport; + +static const uint8_t switch_pro_string_language[] = { 0x09, 0x04 }; +static const uint8_t switch_pro_string_manufacturer[] = "Open Stick Community"; +static const uint8_t switch_pro_string_product[] = "GP2040-CE (Pro Controller)"; +static const uint8_t switch_pro_string_version[] = "000000000001"; + +static const uint8_t *switch_pro_string_descriptors[] __attribute__((unused)) = +{ + switch_pro_string_language, + switch_pro_string_manufacturer, + switch_pro_string_product, + switch_pro_string_version +}; + +static const uint8_t switch_pro_device_descriptor[] = +{ + 0x12, // bLength + 0x01, // bDescriptorType (Device) + 0x00, 0x02, // bcdUSB 2.00 + 0x00, // bDeviceClass (Use class information in the Interface Descriptors) + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + 0x40, // bMaxPacketSize0 64 + 0x7E, 0x05, // idVendor 0x057E + 0x09, 0x20, // idProduct 0x2009 + 0x10, 0x02, // bcdDevice 4.10 + 0x01, // iManufacturer (String Index) + 0x02, // iProduct (String Index) + 0x03, // iSerialNumber (String Index) + 0x01, // bNumConfigurations 1 +}; + +static const uint8_t switch_pro_hid_descriptor[] = +{ + 0x09, // bLength + 0x21, // bDescriptorType (HID) + 0x11, 0x01, // bcdHID 1.11 + 0x00, // bCountryCode + 0x01, // bNumDescriptors + 0x22, // bDescriptorType[0] (HID) + 0xCB, 0x00, // wDescriptorLength[0] 86 +}; + +static const uint8_t switch_pro_configuration_descriptor[] = +{ + 0x09, // bLength + 0x02, // bDescriptorType (Configuration) + 0x29, 0x00, // wTotalLength 41 + 0x01, // bNumInterfaces 1 + 0x01, // bConfigurationValue + 0x00, // iConfiguration (String Index) + 0xA0, // bmAttributes Remote Wakeup + 0xFA, // bMaxPower 500mA + + 0x09, // bLength + 0x04, // bDescriptorType (Interface) + 0x00, // bInterfaceNumber 0 + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints 2 + 0x03, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface (String Index) + + 0x09, // bLength + 0x21, // bDescriptorType (HID) + 0x11, 0x01, // bcdHID 1.11 + 0x00, // bCountryCode + 0x01, // bNumDescriptors + 0x22, // bDescriptorType[0] (HID) + 0xCB, 0x00, // wDescriptorLength[0] 203 + + 0x07, // bLength + 0x05, // bDescriptorType (Endpoint) + 0x05, // bEndpointAddress (IN/D2H) + 0x03, // bmAttributes (Interrupt) + 0x40, 0x00, // wMaxPacketSize 64 + 0x08, // bInterval 8 (unit depends on device speed) + + 0x07, // bLength + 0x05, // bDescriptorType (Endpoint) + 0x01, // bEndpointAddress (OUT/H2D) + 0x03, // bmAttributes (Interrupt) + 0x40, 0x00, // wMaxPacketSize 64 + 0x08, // bInterval 8 (unit depends on device speed) +}; + +static const uint8_t switch_pro_report_descriptor[] = +{ + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x15, 0x00, // Logical Minimum (0) + 0x09, 0x04, // Usage (Joystick) + 0xA1, 0x01, // Collection (Application) + + 0x85, 0x30, // Report ID (48) + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (0x01) + 0x29, 0x0A, // Usage Maximum (0x0A) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x0A, // Report Count (10) + 0x55, 0x00, // Unit Exponent (0) + 0x65, 0x00, // Unit (None) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x0B, // Usage Minimum (0x0B) + 0x29, 0x0E, // Usage Maximum (0x0E) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x04, // Report Count (4) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x01, // Report Size (1) + 0x95, 0x02, // Report Count (2) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x0B, 0x01, 0x00, 0x01, 0x00, // Usage (0x010001) + 0xA1, 0x00, // Collection (Physical) + 0x0B, 0x30, 0x00, 0x01, 0x00, // Usage (0x010030) + 0x0B, 0x31, 0x00, 0x01, 0x00, // Usage (0x010031) + 0x0B, 0x32, 0x00, 0x01, 0x00, // Usage (0x010032) + 0x0B, 0x35, 0x00, 0x01, 0x00, // Usage (0x010035) + 0x15, 0x00, // Logical Minimum (0) + 0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534) + 0x75, 0x10, // Report Size (16) + 0x95, 0x04, // Report Count (4) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, // End Collection + 0x0B, 0x39, 0x00, 0x01, 0x00, // Usage (0x010039) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x07, // Logical Maximum (7) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x3B, 0x01, // Physical Maximum (315) + 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter) + 0x75, 0x04, // Report Size (4) + 0x95, 0x01, // Report Count (1) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x0F, // Usage Minimum (0x0F) + 0x29, 0x12, // Usage Maximum (0x12) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x04, // Report Count (4) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x08, // Report Size (8) + 0x95, 0x34, // Report Count (52) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) + + 0x85, 0x21, // Report ID (33) + 0x09, 0x01, // Usage (0x01) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + + 0x85, 0x81, // Report ID (-127) + 0x09, 0x02, // Usage (0x02) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + + 0x85, 0x01, // Report ID (1) + 0x09, 0x03, // Usage (0x03) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) + 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + + 0x85, 0x10, // Report ID (16) + 0x09, 0x04, // Usage (0x04) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) + 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + + 0x85, 0x80, // Report ID (-128) + 0x09, 0x05, // Usage (0x05) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) + 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + + 0x85, 0x82, // Report ID (-126) + 0x09, 0x06, // Usage (0x06) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) + 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + + 0xC0, // End Collection +}; diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp new file mode 100644 index 0000000..f2e5df2 --- /dev/null +++ b/switch_pro_driver.cpp @@ -0,0 +1,663 @@ +#include "switch_pro_driver.h" + +#include +#include +#include +#include "pico/rand.h" +#include "pico/time.h" +#include "tusb.h" + +// force a report to be sent every X ms +#define SWITCH_PRO_KEEPALIVE_TIMER 5 + +static SwitchInputState g_input_state{ + false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, + SWITCH_PRO_JOYSTICK_MID, SWITCH_PRO_JOYSTICK_MID, + SWITCH_PRO_JOYSTICK_MID, SWITCH_PRO_JOYSTICK_MID}; + +static uint8_t report_buffer[SWITCH_PRO_ENDPOINT_SIZE] = {}; +static uint8_t last_report[SWITCH_PRO_ENDPOINT_SIZE] = {}; +static SwitchProReport switch_report{}; +static uint8_t last_report_counter = 0; +static uint32_t last_report_timer = 0; +static bool is_ready = false; +static bool is_initialized = false; +static bool is_report_queued = false; +static bool report_sent = false; +static uint8_t queued_report_id = 0; +static uint8_t handshake_counter = 0; + +static SwitchDeviceInfo device_info{}; +static uint8_t player_id = 0; +static uint8_t input_mode = 0x30; +static bool is_imu_enabled = false; +static bool is_vibration_enabled = false; + +static uint16_t leftMinX, leftMinY; +static uint16_t leftCenX, leftCenY; +static uint16_t leftMaxX, leftMaxY; +static uint16_t rightMinX, rightMinY; +static uint16_t rightCenX, rightCenY; +static uint16_t rightMaxX, rightMaxY; + +static const uint8_t factory_config_data[0xEFF] = { + // serial number + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + + 0xFF, 0xFF, + + // device type + SWITCH_TYPE_PRO_CONTROLLER, + + // unknown + 0xA0, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + + // color options + 0x02, + + 0xFF, 0xFF, 0xFF, 0xFF, + + // config & calibration 1 + 0xE3, 0xFF, 0x39, 0xFF, 0xED, 0x01, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x40, 0x09, 0x00, 0xEA, 0xFF, + 0xA1, 0xFF, 0x3B, 0x34, 0x3B, 0x34, 0x3B, 0x34, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + + // config & calibration 2 + // left stick + 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, 0xa4, 0x46, + 0x6a, + + // right stick + 0x00, 0x08, 0x80, 0xa4, 0x46, 0x6a, 0xa4, 0x46, + 0x6a, + + 0xFF, + + // body color + 0x1B, 0x1B, 0x1D, + + // button color + 0xFF, 0xFF, 0xFF, + + // left grip color + 0xEC, 0x00, 0x8C, + + // right grip color + 0xEC, 0x00, 0x8C, + + 0x01, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, + + 0x50, 0xFD, 0x00, 0x00, 0xC6, 0x0F, + 0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14, + 0x54, 0x41, 0x15, 0x54, 0xC7, 0x79, 0x9C, 0x33, + 0x36, 0x63, + + 0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14, + 0x54, 0x41, 0x15, 0x54, + + 0xC7, + + 0x79, + + 0x9C, + + 0x33, + + 0x36, + + 0x63, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF +}; + +static const uint8_t user_calibration_data[0x3F] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + + // Left Stick + 0xB2, 0xA1, 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, + 0xa4, 0x46, 0x6a, + + // Right Stick + 0xB2, 0xA1, 0x00, 0x08, 0x80, 0xa4, 0x46, 0x6a, + 0xa4, 0x46, 0x6a, + + // Motion + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static const SwitchFactoryConfig* factory_config = reinterpret_cast(factory_config_data); +static const SwitchUserCalibration* user_calibration [[maybe_unused]] = reinterpret_cast(user_calibration_data); + +static std::map spi_flash_data = { + {0x6000, factory_config_data}, + {0x8000, user_calibration_data} +}; + +static inline uint16_t scale16To12(uint16_t pos) { return pos >> 4; } + +static SwitchInputState make_neutral_state() { + SwitchInputState s{}; + s.lx = SWITCH_PRO_JOYSTICK_MID; + s.ly = SWITCH_PRO_JOYSTICK_MID; + s.rx = SWITCH_PRO_JOYSTICK_MID; + s.ry = SWITCH_PRO_JOYSTICK_MID; + return s; +} + +static void send_identify() { + memset(report_buffer, 0x00, sizeof(report_buffer)); + report_buffer[0] = REPORT_USB_INPUT_81; + report_buffer[1] = IDENTIFY; + report_buffer[2] = 0x00; + report_buffer[3] = device_info.controllerType; + for (uint8_t i = 0; i < 6; i++) { + report_buffer[4 + i] = device_info.macAddress[5 - i]; + } +} + +static bool send_report(uint8_t reportID, const void* reportData, uint16_t reportLength) { + bool result = tud_hid_report(reportID, reportData, reportLength); + if (last_report_counter < 255) { + last_report_counter++; + } else { + last_report_counter = 0; + } + return result; +} + +static void read_spi_flash(uint8_t* dest, uint32_t address, uint8_t size) { + uint32_t addressBank = address & 0xFFFFFF00; + uint32_t addressOffset = address & 0x000000FF; + auto it = spi_flash_data.find(addressBank); + + if (it != spi_flash_data.end()) { + const uint8_t* data = it->second; + memcpy(dest, data + addressOffset, size); + } else { + memset(dest, 0xFF, size); + } +} + +static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) { + bool canSend = false; + + switch (switchReportSubID) { + case IDENTIFY: + send_identify(); + canSend = true; + break; + case HANDSHAKE: + report_buffer[0] = REPORT_USB_INPUT_81; + report_buffer[1] = HANDSHAKE; + canSend = true; + break; + case BAUD_RATE: + report_buffer[0] = REPORT_USB_INPUT_81; + report_buffer[1] = BAUD_RATE; + canSend = true; + break; + case DISABLE_USB_TIMEOUT: + report_buffer[0] = REPORT_OUTPUT_30; + report_buffer[1] = switchReportSubID; + //if (handshakeCounter < 4) { + // handshakeCounter++; + //} else { + is_ready = true; + //} + canSend = true; + break; + case ENABLE_USB_TIMEOUT: + report_buffer[0] = REPORT_OUTPUT_30; + report_buffer[1] = switchReportSubID; + canSend = true; + break; + default: + report_buffer[0] = REPORT_OUTPUT_30; + report_buffer[1] = switchReportSubID; + canSend = true; + break; + } + + if (canSend) is_report_queued = true; +} + +static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) { + uint8_t commandID = reportData[10]; + uint32_t spiReadAddress = 0; + uint8_t spiReadSize = 0; + bool canSend = false; + + report_buffer[0] = REPORT_OUTPUT_21; + report_buffer[1] = last_report_counter; + memcpy(report_buffer + 2, &switch_report.inputs, sizeof(SwitchInputReport)); + + switch (commandID) { + case GET_CONTROLLER_STATE: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + report_buffer[15] = 0x03; + canSend = true; + break; + case BLUETOOTH_PAIR_REQUEST: + report_buffer[13] = 0x81; + report_buffer[14] = commandID; + report_buffer[15] = 0x03; + canSend = true; + break; + case REQUEST_DEVICE_INFO: + report_buffer[13] = 0x82; + report_buffer[14] = 0x02; + memcpy(&report_buffer[15], &device_info, sizeof(device_info)); + canSend = true; + break; + case SET_MODE: + input_mode = reportData[11]; + report_buffer[13] = 0x80; + report_buffer[14] = 0x03; + report_buffer[15] = input_mode; + canSend = true; + break; + case TRIGGER_BUTTONS: + report_buffer[13] = 0x83; + report_buffer[14] = 0x04; + canSend = true; + break; + case SET_SHIPMENT: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + canSend = true; + break; + case SPI_READ: + spiReadAddress = (reportData[14] << 24) | (reportData[13] << 16) | (reportData[12] << 8) | (reportData[11]); + spiReadSize = reportData[15]; + report_buffer[13] = 0x90; + report_buffer[14] = reportData[10]; + report_buffer[15] = reportData[11]; + report_buffer[16] = reportData[12]; + report_buffer[17] = reportData[13]; + report_buffer[18] = reportData[14]; + report_buffer[19] = reportData[15]; + read_spi_flash(&report_buffer[20], spiReadAddress, spiReadSize); + canSend = true; + break; + case SET_NFC_IR_CONFIG: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + canSend = true; + break; + case SET_NFC_IR_STATE: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + canSend = true; + break; + case SET_PLAYER_LIGHTS: + player_id = reportData[11]; + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + canSend = true; + break; + case GET_PLAYER_LIGHTS: + player_id = reportData[11]; + report_buffer[13] = 0xB0; + report_buffer[14] = commandID; + report_buffer[15] = player_id; + canSend = true; + break; + case COMMAND_UNKNOWN_33: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + report_buffer[15] = 0x03; + canSend = true; + break; + case SET_HOME_LIGHT: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + report_buffer[15] = 0x00; + canSend = true; + break; + case TOGGLE_IMU: + is_imu_enabled = reportData[11]; + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + report_buffer[15] = 0x00; + canSend = true; + break; + case IMU_SENSITIVITY: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + canSend = true; + break; + case ENABLE_VIBRATION: + is_vibration_enabled = reportData[11]; + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + report_buffer[15] = 0x00; + canSend = true; + break; + case READ_IMU: + report_buffer[13] = 0xC0; + report_buffer[14] = commandID; + report_buffer[15] = reportData[11]; + report_buffer[16] = reportData[12]; + canSend = true; + break; + case GET_VOLTAGE: + report_buffer[13] = 0xD0; + report_buffer[14] = 0x50; + report_buffer[15] = 0x83; + report_buffer[16] = 0x06; + canSend = true; + break; + default: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + report_buffer[15] = 0x03; + canSend = true; + break; + } + + if (canSend) is_report_queued = true; +} + +static void update_switch_report_from_state() { + switch_report.inputs.dpadUp = g_input_state.dpad_up; + switch_report.inputs.dpadDown = g_input_state.dpad_down; + switch_report.inputs.dpadLeft = g_input_state.dpad_left; + switch_report.inputs.dpadRight = g_input_state.dpad_right; + + switch_report.inputs.chargingGrip = 1; + + switch_report.inputs.buttonY = g_input_state.button_y; + switch_report.inputs.buttonX = g_input_state.button_x; + switch_report.inputs.buttonB = g_input_state.button_b; + switch_report.inputs.buttonA = g_input_state.button_a; + switch_report.inputs.buttonRightSR = 0; + switch_report.inputs.buttonRightSL = 0; + switch_report.inputs.buttonR = g_input_state.button_r; + switch_report.inputs.buttonZR = g_input_state.button_zr; + switch_report.inputs.buttonMinus = g_input_state.button_minus; + switch_report.inputs.buttonPlus = g_input_state.button_plus; + switch_report.inputs.buttonThumbR = g_input_state.button_r3; + switch_report.inputs.buttonThumbL = g_input_state.button_l3; + switch_report.inputs.buttonHome = g_input_state.button_home; + switch_report.inputs.buttonCapture = g_input_state.button_capture; + switch_report.inputs.buttonLeftSR = 0; + switch_report.inputs.buttonLeftSL = 0; + switch_report.inputs.buttonL = g_input_state.button_l; + switch_report.inputs.buttonZL = g_input_state.button_zl; + + uint16_t scaleLeftStickX = scale16To12(g_input_state.lx); + uint16_t scaleLeftStickY = scale16To12(g_input_state.ly); + uint16_t scaleRightStickX = scale16To12(g_input_state.rx); + uint16_t scaleRightStickY = scale16To12(g_input_state.ry); + + switch_report.inputs.leftStick.setX(std::min(std::max(scaleLeftStickX,leftMinX), leftMaxX)); + switch_report.inputs.leftStick.setY(-std::min(std::max(scaleLeftStickY,leftMinY), leftMaxY)); + switch_report.inputs.rightStick.setX(std::min(std::max(scaleRightStickX,rightMinX), rightMaxX)); + switch_report.inputs.rightStick.setY(-std::min(std::max(scaleRightStickY,rightMinY), rightMaxY)); + + switch_report.rumbleReport = 0x09; +} + +void switch_pro_init() { + player_id = 0; + last_report_counter = 0; + handshake_counter = 0; + is_ready = false; + is_initialized = false; + is_report_queued = false; + report_sent = false; + + device_info = { + .majorVersion = 0x04, + .minorVersion = 0x91, + .controllerType = SWITCH_TYPE_PRO_CONTROLLER, + .unknown00 = 0x02, + .macAddress = {0x7c, 0xbb, 0x8a, static_cast(get_rand_32() % 0xff), static_cast(get_rand_32() % 0xff), static_cast(get_rand_32() % 0xff)}, + .unknown01 = 0x01, + .storedColors = 0x02, + }; + + switch_report = { + .reportID = 0x30, + .timestamp = 0, + + .inputs { + .connectionInfo = 0, + .batteryLevel = 0x08, + + .buttonY = 0, + .buttonX = 0, + .buttonB = 0, + .buttonA = 0, + .buttonRightSR = 0, + .buttonRightSL = 0, + .buttonR = 0, + .buttonZR = 0, + + .buttonMinus = 0, + .buttonPlus = 0, + .buttonThumbR = 0, + .buttonThumbL = 0, + .buttonHome = 0, + .buttonCapture = 0, + .dummy = 0, + .chargingGrip = 0, + + .dpadDown = 0, + .dpadUp = 0, + .dpadRight = 0, + .dpadLeft = 0, + .buttonLeftSL = 0, + .buttonLeftSR = 0, + .buttonL = 0, + .buttonZL = 0, + .leftStick = {0xFF, 0xF7, 0x7F}, + .rightStick = {0xFF, 0xF7, 0x7F}, + }, + .rumbleReport = 0, + .imuData = {0x00}, + .padding = {0x00} + }; + + last_report_timer = to_ms_since_boot(get_absolute_time()); + + factory_config->leftStickCalibration.getRealMin(leftMinX, leftMinY); + factory_config->leftStickCalibration.getCenter(leftCenX, leftCenY); + factory_config->leftStickCalibration.getRealMax(leftMaxX, leftMaxY); + factory_config->rightStickCalibration.getRealMin(rightMinX, rightMinY); + factory_config->rightStickCalibration.getCenter(rightCenX, rightCenY); + factory_config->rightStickCalibration.getRealMax(rightMaxX, rightMaxY); +} + +void switch_pro_set_input(const SwitchInputState& state) { + g_input_state = state; +} + +void switch_pro_task() { + uint32_t now = to_ms_since_boot(get_absolute_time()); + report_sent = false; + + update_switch_report_from_state(); + + if (tud_suspended()) { + tud_remote_wakeup(); + } + + if (is_report_queued) { + if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) { + if (tud_hid_ready() && send_report(queued_report_id, report_buffer, 64) == true ) { + } + is_report_queued = false; + last_report_timer = now; + } + report_sent = true; + } + + if (is_ready && !report_sent) { + if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) { + switch_report.timestamp = last_report_counter; + void * inputReport = &switch_report; + uint16_t report_size = sizeof(switch_report); + if (memcmp(last_report, inputReport, report_size) != 0) { + if (tud_hid_ready() && send_report(0, inputReport, report_size) == true ) { + memcpy(last_report, inputReport, report_size); + report_sent = true; + } + + last_report_timer = now; + } + } + } else { + if (!is_initialized) { + send_identify(); + if (tud_hid_ready() && tud_hid_report(0, report_buffer, 64) == true) { + is_initialized = true; + report_sent = true; + } + + last_report_timer = now; + } + } +} + +bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length) { + // Packet format: 0xAA, buttons(2 LE), hat, lx, ly, rx, ry + if (length < 8 || packet[0] != 0xAA) { + return false; + } + + SwitchProOutReport out{}; + out.buttons = static_cast(packet[1]) | (static_cast(packet[2]) << 8); + out.hat = packet[3]; + out.lx = packet[4]; + out.ly = packet[5]; + out.rx = packet[6]; + out.ry = packet[7]; + + auto expand_axis = [](uint8_t v) -> uint16_t { + return static_cast(v) << 8 | v; + }; + + SwitchInputState state = make_neutral_state(); + + switch (out.hat) { + case SWITCH_PRO_HAT_UP: state.dpad_up = true; break; + case SWITCH_PRO_HAT_UPRIGHT: state.dpad_up = true; state.dpad_right = true; break; + case SWITCH_PRO_HAT_RIGHT: state.dpad_right = true; break; + case SWITCH_PRO_HAT_DOWNRIGHT: state.dpad_down = true; state.dpad_right = true; break; + case SWITCH_PRO_HAT_DOWN: state.dpad_down = true; break; + case SWITCH_PRO_HAT_DOWNLEFT: state.dpad_down = true; state.dpad_left = true; break; + case SWITCH_PRO_HAT_LEFT: state.dpad_left = true; break; + case SWITCH_PRO_HAT_UPLEFT: state.dpad_up = true; state.dpad_left = true; break; + default: break; + } + + state.button_y = out.buttons & SWITCH_PRO_MASK_Y; + state.button_x = out.buttons & SWITCH_PRO_MASK_X; + state.button_b = out.buttons & SWITCH_PRO_MASK_B; + state.button_a = out.buttons & SWITCH_PRO_MASK_A; + state.button_r = out.buttons & SWITCH_PRO_MASK_R; + state.button_zr = out.buttons & SWITCH_PRO_MASK_ZR; + state.button_plus = out.buttons & SWITCH_PRO_MASK_PLUS; + state.button_minus = out.buttons & SWITCH_PRO_MASK_MINUS; + state.button_r3 = out.buttons & SWITCH_PRO_MASK_R3; + state.button_l3 = out.buttons & SWITCH_PRO_MASK_L3; + state.button_home = out.buttons & SWITCH_PRO_MASK_HOME; + state.button_capture = out.buttons & SWITCH_PRO_MASK_CAPTURE; + state.button_zl = out.buttons & SWITCH_PRO_MASK_ZL; + state.button_l = out.buttons & SWITCH_PRO_MASK_L; + + state.lx = expand_axis(out.lx); + state.ly = expand_axis(out.ly); + state.rx = expand_axis(out.rx); + state.ry = expand_axis(out.ry); + + switch_pro_set_input(state); + return true; +} + +// HID callbacks +uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { + (void)instance; + (void)report_id; + (void)report_type; + (void)buffer; + (void)reqlen; + return 0; +} + +void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) { + (void)instance; + if (report_type != HID_REPORT_TYPE_OUTPUT) return; + + memset(report_buffer, 0x00, bufsize); + + uint8_t switchReportID = buffer[0]; + uint8_t switchReportSubID = buffer[1]; + if (switchReportID == REPORT_OUTPUT_00) { + } else if (switchReportID == REPORT_FEATURE) { + queued_report_id = report_id; + handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize); + } else if (switchReportID == REPORT_CONFIGURATION) { + queued_report_id = report_id; + handle_config_report(switchReportID, switchReportSubID, buffer, bufsize); + } else { + } +} + +uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) { + (void)itf; + return switch_pro_report_descriptor; +} + +uint8_t const * tud_descriptor_device_cb(void) { + return switch_pro_device_descriptor; +} + +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { + (void)index; + return switch_pro_configuration_descriptor; +} + +static uint16_t desc_str[32]; + +uint16_t const * tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void)langid; + + uint8_t chr_count; + + if ( index == 0 ) { + memcpy(&desc_str[1], switch_pro_string_language, 2); + chr_count = 1; + } else { + if ( index >= sizeof(switch_pro_string_descriptors)/sizeof(switch_pro_string_descriptors[0]) ) return nullptr; + + const uint8_t *str = switch_pro_string_descriptors[index]; + + chr_count = 0; + while ( str[chr_count] ) chr_count++; + if ( chr_count > 31 ) chr_count = 31; + + for(uint8_t i=0; i +#include +#include "switch_pro_descriptors.h" + +typedef struct { + bool dpad_up; + bool dpad_down; + bool dpad_left; + bool dpad_right; + + bool button_a; + bool button_b; + bool button_x; + bool button_y; + bool button_l; + bool button_r; + bool button_zl; + bool button_zr; + bool button_plus; + bool button_minus; + bool button_home; + bool button_capture; + bool button_l3; + bool button_r3; + + uint16_t lx; // 0-65535 + uint16_t ly; + uint16_t rx; + uint16_t ry; +} SwitchInputState; + +// Initialize USB state and calibration before entering the main loop. +void switch_pro_init(); + +// Update the desired controller state for the next USB report. +void switch_pro_set_input(const SwitchInputState& state); + +// Drive the Switch Pro USB state machine; call this frequently in the main loop. +void switch_pro_task(); + +// Convert a packed UART message into controller state (returns true if parsed). +bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length); diff --git a/tusb_config.h b/tusb_config.h new file mode 100644 index 0000000..ff97f2e --- /dev/null +++ b/tusb_config.h @@ -0,0 +1,38 @@ +// TinyUSB configuration tailored for a single Switch Pro style HID interface. +// Data is derived from TinyUSB examples and tuned for a 64-byte HID endpoint. +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED) +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4))) +#endif + +#define CFG_TUD_ENDPOINT0_SIZE 64 + +// Device class configuration +#define CFG_TUD_HID 1 +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +#define CFG_TUD_HID_EP_BUFSIZE 64 + +#ifdef __cplusplus +} +#endif + +#endif // _TUSB_CONFIG_H_ From 7c0d057bcd60572305c6103533f4dceecd3af4d2 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 11:20:19 -0700 Subject: [PATCH 002/134] Initial commit --- .gitignore | 3 + .vscode/c_cpp_properties.json | 22 ++ .vscode/cmake-kits.json | 15 + .vscode/extensions.json | 9 + .vscode/launch.json | 50 +++ .vscode/settings.json | 40 ++ .vscode/tasks.json | 102 ++++++ CMakeLists.txt | 64 ++++ pico_sdk_import.cmake | 121 +++++++ switch-pico.cpp | 76 ++++ switch_pro_descriptors.h | 511 ++++++++++++++++++++++++++ switch_pro_driver.cpp | 663 ++++++++++++++++++++++++++++++++++ switch_pro_driver.h | 50 +++ tusb_config.h | 38 ++ 14 files changed, 1764 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/cmake-kits.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 CMakeLists.txt create mode 100644 pico_sdk_import.cmake create mode 100644 switch-pico.cpp create mode 100644 switch_pro_descriptors.h create mode 100644 switch_pro_driver.cpp create mode 100644 switch_pro_driver.h create mode 100644 tusb_config.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ed84ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +GP2040-CE +build +!.vscode/* diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..0a6cd5a --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.2.0/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} diff --git a/.vscode/cmake-kits.json b/.vscode/cmake-kits.json new file mode 100644 index 0000000..b0f3815 --- /dev/null +++ b/.vscode/cmake-kits.json @@ -0,0 +1,15 @@ +[ + { + "name": "Pico", + "compilers": { + "C": "${command:raspberry-pi-pico.getCompilerPath}", + "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}" + }, + "environmentVariables": { + "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}" + }, + "cmakeSettings": { + "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}" + } + } +] \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..a940d7c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "marus25.cortex-debug", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.vscode-serial-monitor", + "raspberry-pi.raspberry-pi-pico" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..424aa71 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,50 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Pico Debug (Cortex-Debug)", + "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/${command:raspberry-pi-pico.getTarget}.cfg" + ], + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ], + "openOCDLaunchCommands": [ + "adapter speed 5000" + ] + }, + { + "name": "Pico Debug (Cortex-Debug with external OpenOCD)", + "cwd": "${workspaceRoot}", + "executable": "${command:raspberry-pi-pico.launchTargetPath}", + "request": "launch", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", + "device": "${command:raspberry-pi-pico.getChipUppercase}", + "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd", + "runToEntryPoint": "main", + // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected + // Also works fine for flash binaries + "overrideLaunchCommands": [ + "monitor reset init", + "load \"${command:raspberry-pi-pico.launchTargetPath}\"" + ] + }, + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..cd4187a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "cmake.showSystemKits": false, + "cmake.options.statusBarVisibility": "hidden", + "cmake.options.advanced": { + "build": { + "statusBarVisibility": "hidden" + }, + "launch": { + "statusBarVisibility": "hidden" + }, + "debug": { + "statusBarVisibility": "hidden" + } + }, + "cmake.configureOnEdit": true, + "cmake.automaticReconfigure": true, + "cmake.configureOnOpen": true, + "cmake.generator": "Ninja", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "C_Cpp.debugShortcut": false, + "terminal.integrated.env.windows": { + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + }, + "terminal.integrated.env.osx": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "terminal.integrated.env.linux": { + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + }, + "raspberry-pi-pico.cmakeAutoConfigure": false, + "raspberry-pi-pico.useCmakeTools": true, + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", + "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja" +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..d1b3193 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Compile Project", + "type": "process", + "isBuildCommand": true, + "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja", + "args": ["-C", "${workspaceFolder}/build"], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": "$gcc", + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe" + } + }, + { + "label": "Run Project", + "type": "process", + "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool", + "args": [ + "load", + "${command:raspberry-pi-pico.launchTargetPath}", + "-fx" + ], + "presentation": { + "reveal": "always", + "panel": "dedicated" + }, + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe" + } + }, + { + "label": "Flash", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getTarget}.cfg", + "-c", + "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "Rescue Reset", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/${command:raspberry-pi-pico.getChip}-rescue.cfg", + "-c", + "adapter speed 5000; reset halt; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + }, + { + "label": "RISC-V Reset (RP2350)", + "type": "process", + "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + "args": [ + "-s", + "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts", + "-c", + "set USE_CORE { rv0 rv1 cm0 cm1 }", + "-f", + "interface/cmsis-dap.cfg", + "-f", + "target/rp2350.cfg", + "-c", + "adapter speed 5000; init;", + "-c", + "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];", + "-c", + "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit" + ], + "problemMatcher": [], + "windows": { + "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe", + } + } + ] +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7ff57c6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,64 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.2.0-a4) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +set(PICO_BOARD pico CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(switch-pico C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(switch-pico + switch-pico.cpp + switch_pro_driver.cpp +) + +pico_set_program_name(switch-pico "switch-pico") +pico_set_program_version(switch-pico "0.1") + +# Modify the below lines to enable/disable output over UART/USB +pico_enable_stdio_uart(switch-pico 0) +pico_enable_stdio_usb(switch-pico 0) + +# Add the standard library to the build +target_link_libraries(switch-pico + pico_stdlib + tinyusb_device + tinyusb_board + hardware_uart + pico_rand +) + +# Add the standard include files to the build +target_include_directories(switch-pico PRIVATE + ${CMAKE_CURRENT_LIST_DIR} +) + +pico_add_extra_outputs(switch-pico) diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/switch-pico.cpp b/switch-pico.cpp new file mode 100644 index 0000000..88f9fe6 --- /dev/null +++ b/switch-pico.cpp @@ -0,0 +1,76 @@ +#include +#include "bsp/board.h" +#include "hardware/uart.h" +#include "pico/stdlib.h" +#include "tusb.h" +#include "switch_pro_driver.h" + +// UART1 is reserved for external input frames from the host PC. +#define UART_ID uart1 +#define BAUD_RATE 115200 +#define UART_TX_PIN 4 +#define UART_RX_PIN 5 + +static void init_uart_input() { + uart_init(UART_ID, BAUD_RATE); + gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); + gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART); + uart_set_format(UART_ID, 8, 1, UART_PARITY_NONE); +} + +static SwitchInputState neutral_input() { + SwitchInputState state{}; + state.lx = SWITCH_PRO_JOYSTICK_MID; + state.ly = SWITCH_PRO_JOYSTICK_MID; + state.rx = SWITCH_PRO_JOYSTICK_MID; + state.ry = SWITCH_PRO_JOYSTICK_MID; + return state; +} + +// Consume UART bytes and forward complete frames to the Switch Pro driver. +static void poll_uart_frames() { + static uint8_t buffer[8]; + static uint8_t index = 0; + static absolute_time_t last_byte_time = {0}; + static bool has_last_byte = false; + + while (uart_is_readable(UART_ID)) { + uint8_t byte = uart_getc(UART_ID); + + uint64_t now = to_ms_since_boot(get_absolute_time()); + if (has_last_byte && (now - to_ms_since_boot(last_byte_time)) > 20) { + index = 0; // stale data, restart frame + } + last_byte_time = get_absolute_time(); + has_last_byte = true; + + if (index == 0) { + if (byte != 0xAA) { + continue; // wait for start-of-frame marker + } + } + + buffer[index++] = byte; + if (index >= sizeof(buffer)) { + switch_pro_apply_uart_packet(buffer, sizeof(buffer)); + index = 0; + } + } +} + +int main() { + board_init(); + stdio_init_all(); + + init_uart_input(); + + tusb_init(); + switch_pro_init(); + switch_pro_set_input(neutral_input()); + + while (true) { + tud_task(); // USB device tasks + poll_uart_frames(); // Pull controller state from UART1 + switch_pro_task(); // Push state to the Switch host + } +} diff --git a/switch_pro_descriptors.h b/switch_pro_descriptors.h new file mode 100644 index 0000000..be1a667 --- /dev/null +++ b/switch_pro_descriptors.h @@ -0,0 +1,511 @@ +/* + * Switch Pro controller descriptor and data definitions. + * Copied from the GP2040-CE project to mirror the behaviour of its + * SwitchProDriver without pulling in the rest of the codebase. + */ + +#pragma once + +#include + +#define SWITCH_PRO_ENDPOINT_SIZE 64 + +// HAT report (4 bits) +#define SWITCH_PRO_HAT_UP 0x00 +#define SWITCH_PRO_HAT_UPRIGHT 0x01 +#define SWITCH_PRO_HAT_RIGHT 0x02 +#define SWITCH_PRO_HAT_DOWNRIGHT 0x03 +#define SWITCH_PRO_HAT_DOWN 0x04 +#define SWITCH_PRO_HAT_DOWNLEFT 0x05 +#define SWITCH_PRO_HAT_LEFT 0x06 +#define SWITCH_PRO_HAT_UPLEFT 0x07 +#define SWITCH_PRO_HAT_NOTHING 0x08 + +#define SWITCH_PRO_MASK_ZR (1U << 7) +#define SWITCH_PRO_MASK_R (1U << 6) +#define SWITCH_PRO_MASK_A (1U << 3) +#define SWITCH_PRO_MASK_B (1U << 2) +#define SWITCH_PRO_MASK_X (1U << 1) +#define SWITCH_PRO_MASK_Y 1U + +#define SWITCH_PRO_MASK_CAPTURE (1U << 5) +#define SWITCH_PRO_MASK_HOME (1U << 4) +#define SWITCH_PRO_MASK_L3 (1U << 3) +#define SWITCH_PRO_MASK_R3 (1U << 2) +#define SWITCH_PRO_MASK_PLUS (1U << 1) +#define SWITCH_PRO_MASK_MINUS 1U + +#define SWITCH_PRO_MASK_ZL (1U << 7) +#define SWITCH_PRO_MASK_L (1U << 6) + +#define SWITCH_PRO_JOYSTICK_MIN 0x0000 +#define SWITCH_PRO_JOYSTICK_MID 0x7FFF +#define SWITCH_PRO_JOYSTICK_MAX 0xFFFF + +typedef enum { + REPORT_OUTPUT_00 = 0x00, + REPORT_FEATURE = 0x01, + REPORT_OUTPUT_10 = 0x10, + REPORT_OUTPUT_21 = 0x21, + REPORT_OUTPUT_30 = 0x30, + REPORT_CONFIGURATION = 0x80, + REPORT_USB_INPUT_81 = 0x81, +} SwitchReportID; + +typedef enum { + IDENTIFY = 0x01, + HANDSHAKE, + BAUD_RATE, + DISABLE_USB_TIMEOUT, + ENABLE_USB_TIMEOUT +} SwitchOutputSubtypes; + +typedef enum { + GET_CONTROLLER_STATE = 0x00, + BLUETOOTH_PAIR_REQUEST = 0x01, + REQUEST_DEVICE_INFO = 0x02, + SET_MODE = 0x03, + TRIGGER_BUTTONS = 0x04, + SET_SHIPMENT = 0x08, + SPI_READ = 0x10, + SET_NFC_IR_CONFIG = 0x21, + SET_NFC_IR_STATE = 0x22, + SET_PLAYER_LIGHTS = 0x30, + GET_PLAYER_LIGHTS = 0x31, + COMMAND_UNKNOWN_33 = 0x33, + SET_HOME_LIGHT = 0x38, + TOGGLE_IMU = 0x40, + IMU_SENSITIVITY = 0x41, + READ_IMU = 0x43, + ENABLE_VIBRATION = 0x48, + GET_VOLTAGE = 0x50, +} SwitchCommands; + +typedef struct { + uint8_t data[3]; + + void setX(uint16_t x) { + data[0] = x & 0xFF; + data[1] = (data[1] & 0xF0) | ((x >> 8) & 0x0F); + } + + void setY(uint16_t y) { + data[1] = (data[1] & 0x0F) | ((y & 0x0F) << 4); + data[2] = (y >> 4) & 0xFF; + } + + uint16_t getX() { + return static_cast(data[0]) | ((data[1] & 0x0F) << 8); + } + + uint16_t getY() { + return static_cast((data[1] >> 4)) | (data[2] << 4); + } +} SwitchAnalog; + +// left and right calibration are stored differently for some reason, so two structs +typedef struct { + uint8_t data[9]; + + void getMin(uint16_t& x, uint16_t& y) const { packCalib(6, x, y); } + void getCenter(uint16_t& x, uint16_t& y) const { packCalib(3, x, y); } + void getMax(uint16_t& x, uint16_t& y) const { packCalib(0, x, y); } + + void getRealMin(uint16_t& x, uint16_t& y) const { + uint16_t minX, minY; + uint16_t cenX, cenY; + + getMin(minX, minY); + getCenter(cenX, cenY); + + x = cenX - minX; + y = cenY - minY; + } + + void getRealMax(uint16_t& x, uint16_t& y) const { + uint16_t maxX, maxY; + uint16_t cenX, cenY; + + getMax(maxX, maxY); + getCenter(cenX, cenY); + + x = cenX + maxX; + y = cenY + maxY; + } + + void packCalib(uint8_t offset, uint16_t& x, uint16_t& y) const { + x = static_cast(data[offset]) | ((data[offset + 1] & 0x0F) << 8); + y = static_cast(data[offset + 2] << 4) | (data[offset + 1] >> 4); + } +} SwitchLeftCalibration; + +typedef struct { + uint8_t data[9]; + + void getMin(uint16_t& x, uint16_t& y) const { packCalib(3, x, y); } + void getCenter(uint16_t& x, uint16_t& y) const { packCalib(0, x, y); } + void getMax(uint16_t& x, uint16_t& y) const { packCalib(6, x, y); } + + void getRealMin(uint16_t& x, uint16_t& y) const { + uint16_t minX, minY; + uint16_t cenX, cenY; + + getMin(minX, minY); + getCenter(cenX, cenY); + + x = cenX - minX; + y = cenY - minY; + } + + void getRealMax(uint16_t& x, uint16_t& y) const { + uint16_t maxX, maxY; + uint16_t cenX, cenY; + + getMax(maxX, maxY); + getCenter(cenX, cenY); + + x = cenX + maxX; + y = cenY + maxY; + } + + void packCalib(uint8_t offset, uint16_t& x, uint16_t& y) const { + x = static_cast(data[offset]) | ((data[offset + 1] & 0x0F) << 8); + y = static_cast(data[offset + 2] << 4) | (data[offset + 1] >> 4); + } +} SwitchRightCalibration; + +typedef struct +{ + uint8_t red; + uint8_t green; + uint8_t blue; +} SwitchColorDefinition; + +typedef struct __attribute((packed, aligned(1))) +{ + uint8_t serialNumber[16]; + + uint8_t unknown00[2]; + + uint8_t deviceType; + + uint8_t unknown01; // usually 0xA0 + + uint8_t unknown02[7]; + + uint8_t colorInfo; // 0 = default colors + + uint8_t unknown03[4]; + + uint8_t motionCalibration[24]; + + uint8_t unknown04[5]; + + SwitchLeftCalibration leftStickCalibration; + + SwitchRightCalibration rightStickCalibration; + + uint8_t unknown08; + + SwitchColorDefinition bodyColor; + + SwitchColorDefinition buttonColor; + + SwitchColorDefinition leftGripColor; + + SwitchColorDefinition rightGripColor; + + uint8_t unknown06[37]; + + uint8_t motionHorizontalOffsets[6]; + + uint8_t stickParams1[17]; + + uint8_t stickParams2[17]; + + uint8_t unknown07[0xE57]; +} SwitchFactoryConfig; + +typedef struct __attribute((packed, aligned(1))) +{ + uint8_t unknown00[16]; + + uint8_t leftCalibrationMagic[2]; + SwitchLeftCalibration leftCalibration; + + uint8_t rightCalibrationMagic[2]; + SwitchRightCalibration rightCalibration; + + uint8_t motionCalibrationMagic[2]; + uint8_t motionCalibration[24]; +} SwitchUserCalibration; + +typedef struct __attribute((packed, aligned(1))) +{ + uint8_t connectionInfo : 4; + uint8_t batteryLevel : 4; + + // byte 00 + uint8_t buttonY : 1; + uint8_t buttonX : 1; + uint8_t buttonB : 1; + uint8_t buttonA : 1; + uint8_t buttonRightSR : 1; + uint8_t buttonRightSL : 1; + uint8_t buttonR : 1; + uint8_t buttonZR : 1; + + // byte 01 + uint8_t buttonMinus : 1; + uint8_t buttonPlus : 1; + uint8_t buttonThumbR : 1; + uint8_t buttonThumbL : 1; + uint8_t buttonHome : 1; + uint8_t buttonCapture : 1; + uint8_t dummy : 1; + uint8_t chargingGrip : 1; + + // byte 02 + uint8_t dpadDown : 1; + uint8_t dpadUp : 1; + uint8_t dpadRight : 1; + uint8_t dpadLeft : 1; + uint8_t buttonLeftSL : 1; + uint8_t buttonLeftSR : 1; + uint8_t buttonL : 1; + uint8_t buttonZL : 1; + + SwitchAnalog leftStick; + SwitchAnalog rightStick; +} SwitchInputReport; + +typedef struct __attribute((packed, aligned(1))) +{ + uint8_t reportID; + uint8_t timestamp; + SwitchInputReport inputs; + uint8_t rumbleReport; + uint8_t imuData[36]; + uint8_t padding[15]; +} SwitchProReport; + +typedef enum { + SWITCH_TYPE_LEFT_JOYCON = 0x01, + SWITCH_TYPE_RIGHT_JOYCON, + SWITCH_TYPE_PRO_CONTROLLER, + SWITCH_TYPE_FAMICOM_LEFT_JOYCON = 0x07, + SWITCH_TYPE_FAMICOM_RIGHT_JOYCON = 0x08, + SWITCH_TYPE_NES_LEFT_JOYCON = 0x09, + SWITCH_TYPE_NES_RIGHT_JOYCON = 0x0A, + SWITCH_TYPE_SNES = 0x0B, + SWITCH_TYPE_N64 = 0x0C, +} SwitchControllerType; + +typedef struct { + uint8_t majorVersion; + uint8_t minorVersion; + uint8_t controllerType; + uint8_t unknown00; + uint8_t macAddress[6]; + uint8_t unknown01; + uint8_t storedColors; +} SwitchDeviceInfo; + +typedef struct +{ + uint16_t buttons; + uint8_t hat; + uint8_t lx; + uint8_t ly; + uint8_t rx; + uint8_t ry; +} SwitchProOutReport; + +static const uint8_t switch_pro_string_language[] = { 0x09, 0x04 }; +static const uint8_t switch_pro_string_manufacturer[] = "Open Stick Community"; +static const uint8_t switch_pro_string_product[] = "GP2040-CE (Pro Controller)"; +static const uint8_t switch_pro_string_version[] = "000000000001"; + +static const uint8_t *switch_pro_string_descriptors[] __attribute__((unused)) = +{ + switch_pro_string_language, + switch_pro_string_manufacturer, + switch_pro_string_product, + switch_pro_string_version +}; + +static const uint8_t switch_pro_device_descriptor[] = +{ + 0x12, // bLength + 0x01, // bDescriptorType (Device) + 0x00, 0x02, // bcdUSB 2.00 + 0x00, // bDeviceClass (Use class information in the Interface Descriptors) + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + 0x40, // bMaxPacketSize0 64 + 0x7E, 0x05, // idVendor 0x057E + 0x09, 0x20, // idProduct 0x2009 + 0x10, 0x02, // bcdDevice 4.10 + 0x01, // iManufacturer (String Index) + 0x02, // iProduct (String Index) + 0x03, // iSerialNumber (String Index) + 0x01, // bNumConfigurations 1 +}; + +static const uint8_t switch_pro_hid_descriptor[] = +{ + 0x09, // bLength + 0x21, // bDescriptorType (HID) + 0x11, 0x01, // bcdHID 1.11 + 0x00, // bCountryCode + 0x01, // bNumDescriptors + 0x22, // bDescriptorType[0] (HID) + 0xCB, 0x00, // wDescriptorLength[0] 86 +}; + +static const uint8_t switch_pro_configuration_descriptor[] = +{ + 0x09, // bLength + 0x02, // bDescriptorType (Configuration) + 0x29, 0x00, // wTotalLength 41 + 0x01, // bNumInterfaces 1 + 0x01, // bConfigurationValue + 0x00, // iConfiguration (String Index) + 0xA0, // bmAttributes Remote Wakeup + 0xFA, // bMaxPower 500mA + + 0x09, // bLength + 0x04, // bDescriptorType (Interface) + 0x00, // bInterfaceNumber 0 + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints 2 + 0x03, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface (String Index) + + 0x09, // bLength + 0x21, // bDescriptorType (HID) + 0x11, 0x01, // bcdHID 1.11 + 0x00, // bCountryCode + 0x01, // bNumDescriptors + 0x22, // bDescriptorType[0] (HID) + 0xCB, 0x00, // wDescriptorLength[0] 203 + + 0x07, // bLength + 0x05, // bDescriptorType (Endpoint) + 0x05, // bEndpointAddress (IN/D2H) + 0x03, // bmAttributes (Interrupt) + 0x40, 0x00, // wMaxPacketSize 64 + 0x08, // bInterval 8 (unit depends on device speed) + + 0x07, // bLength + 0x05, // bDescriptorType (Endpoint) + 0x01, // bEndpointAddress (OUT/H2D) + 0x03, // bmAttributes (Interrupt) + 0x40, 0x00, // wMaxPacketSize 64 + 0x08, // bInterval 8 (unit depends on device speed) +}; + +static const uint8_t switch_pro_report_descriptor[] = +{ + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x15, 0x00, // Logical Minimum (0) + 0x09, 0x04, // Usage (Joystick) + 0xA1, 0x01, // Collection (Application) + + 0x85, 0x30, // Report ID (48) + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (0x01) + 0x29, 0x0A, // Usage Maximum (0x0A) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x0A, // Report Count (10) + 0x55, 0x00, // Unit Exponent (0) + 0x65, 0x00, // Unit (None) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x0B, // Usage Minimum (0x0B) + 0x29, 0x0E, // Usage Maximum (0x0E) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x04, // Report Count (4) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x01, // Report Size (1) + 0x95, 0x02, // Report Count (2) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x0B, 0x01, 0x00, 0x01, 0x00, // Usage (0x010001) + 0xA1, 0x00, // Collection (Physical) + 0x0B, 0x30, 0x00, 0x01, 0x00, // Usage (0x010030) + 0x0B, 0x31, 0x00, 0x01, 0x00, // Usage (0x010031) + 0x0B, 0x32, 0x00, 0x01, 0x00, // Usage (0x010032) + 0x0B, 0x35, 0x00, 0x01, 0x00, // Usage (0x010035) + 0x15, 0x00, // Logical Minimum (0) + 0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534) + 0x75, 0x10, // Report Size (16) + 0x95, 0x04, // Report Count (4) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, // End Collection + 0x0B, 0x39, 0x00, 0x01, 0x00, // Usage (0x010039) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x07, // Logical Maximum (7) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x3B, 0x01, // Physical Maximum (315) + 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter) + 0x75, 0x04, // Report Size (4) + 0x95, 0x01, // Report Count (1) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x0F, // Usage Minimum (0x0F) + 0x29, 0x12, // Usage Maximum (0x12) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x04, // Report Count (4) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x08, // Report Size (8) + 0x95, 0x34, // Report Count (52) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) + + 0x85, 0x21, // Report ID (33) + 0x09, 0x01, // Usage (0x01) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + + 0x85, 0x81, // Report ID (-127) + 0x09, 0x02, // Usage (0x02) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + + 0x85, 0x01, // Report ID (1) + 0x09, 0x03, // Usage (0x03) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) + 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + + 0x85, 0x10, // Report ID (16) + 0x09, 0x04, // Usage (0x04) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) + 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + + 0x85, 0x80, // Report ID (-128) + 0x09, 0x05, // Usage (0x05) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) + 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + + 0x85, 0x82, // Report ID (-126) + 0x09, 0x06, // Usage (0x06) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) + 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + + 0xC0, // End Collection +}; diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp new file mode 100644 index 0000000..f2e5df2 --- /dev/null +++ b/switch_pro_driver.cpp @@ -0,0 +1,663 @@ +#include "switch_pro_driver.h" + +#include +#include +#include +#include "pico/rand.h" +#include "pico/time.h" +#include "tusb.h" + +// force a report to be sent every X ms +#define SWITCH_PRO_KEEPALIVE_TIMER 5 + +static SwitchInputState g_input_state{ + false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, + SWITCH_PRO_JOYSTICK_MID, SWITCH_PRO_JOYSTICK_MID, + SWITCH_PRO_JOYSTICK_MID, SWITCH_PRO_JOYSTICK_MID}; + +static uint8_t report_buffer[SWITCH_PRO_ENDPOINT_SIZE] = {}; +static uint8_t last_report[SWITCH_PRO_ENDPOINT_SIZE] = {}; +static SwitchProReport switch_report{}; +static uint8_t last_report_counter = 0; +static uint32_t last_report_timer = 0; +static bool is_ready = false; +static bool is_initialized = false; +static bool is_report_queued = false; +static bool report_sent = false; +static uint8_t queued_report_id = 0; +static uint8_t handshake_counter = 0; + +static SwitchDeviceInfo device_info{}; +static uint8_t player_id = 0; +static uint8_t input_mode = 0x30; +static bool is_imu_enabled = false; +static bool is_vibration_enabled = false; + +static uint16_t leftMinX, leftMinY; +static uint16_t leftCenX, leftCenY; +static uint16_t leftMaxX, leftMaxY; +static uint16_t rightMinX, rightMinY; +static uint16_t rightCenX, rightCenY; +static uint16_t rightMaxX, rightMaxY; + +static const uint8_t factory_config_data[0xEFF] = { + // serial number + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + + 0xFF, 0xFF, + + // device type + SWITCH_TYPE_PRO_CONTROLLER, + + // unknown + 0xA0, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + + // color options + 0x02, + + 0xFF, 0xFF, 0xFF, 0xFF, + + // config & calibration 1 + 0xE3, 0xFF, 0x39, 0xFF, 0xED, 0x01, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x40, 0x09, 0x00, 0xEA, 0xFF, + 0xA1, 0xFF, 0x3B, 0x34, 0x3B, 0x34, 0x3B, 0x34, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + + // config & calibration 2 + // left stick + 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, 0xa4, 0x46, + 0x6a, + + // right stick + 0x00, 0x08, 0x80, 0xa4, 0x46, 0x6a, 0xa4, 0x46, + 0x6a, + + 0xFF, + + // body color + 0x1B, 0x1B, 0x1D, + + // button color + 0xFF, 0xFF, 0xFF, + + // left grip color + 0xEC, 0x00, 0x8C, + + // right grip color + 0xEC, 0x00, 0x8C, + + 0x01, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, + + 0x50, 0xFD, 0x00, 0x00, 0xC6, 0x0F, + 0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14, + 0x54, 0x41, 0x15, 0x54, 0xC7, 0x79, 0x9C, 0x33, + 0x36, 0x63, + + 0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14, + 0x54, 0x41, 0x15, 0x54, + + 0xC7, + + 0x79, + + 0x9C, + + 0x33, + + 0x36, + + 0x63, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF +}; + +static const uint8_t user_calibration_data[0x3F] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + + // Left Stick + 0xB2, 0xA1, 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, + 0xa4, 0x46, 0x6a, + + // Right Stick + 0xB2, 0xA1, 0x00, 0x08, 0x80, 0xa4, 0x46, 0x6a, + 0xa4, 0x46, 0x6a, + + // Motion + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static const SwitchFactoryConfig* factory_config = reinterpret_cast(factory_config_data); +static const SwitchUserCalibration* user_calibration [[maybe_unused]] = reinterpret_cast(user_calibration_data); + +static std::map spi_flash_data = { + {0x6000, factory_config_data}, + {0x8000, user_calibration_data} +}; + +static inline uint16_t scale16To12(uint16_t pos) { return pos >> 4; } + +static SwitchInputState make_neutral_state() { + SwitchInputState s{}; + s.lx = SWITCH_PRO_JOYSTICK_MID; + s.ly = SWITCH_PRO_JOYSTICK_MID; + s.rx = SWITCH_PRO_JOYSTICK_MID; + s.ry = SWITCH_PRO_JOYSTICK_MID; + return s; +} + +static void send_identify() { + memset(report_buffer, 0x00, sizeof(report_buffer)); + report_buffer[0] = REPORT_USB_INPUT_81; + report_buffer[1] = IDENTIFY; + report_buffer[2] = 0x00; + report_buffer[3] = device_info.controllerType; + for (uint8_t i = 0; i < 6; i++) { + report_buffer[4 + i] = device_info.macAddress[5 - i]; + } +} + +static bool send_report(uint8_t reportID, const void* reportData, uint16_t reportLength) { + bool result = tud_hid_report(reportID, reportData, reportLength); + if (last_report_counter < 255) { + last_report_counter++; + } else { + last_report_counter = 0; + } + return result; +} + +static void read_spi_flash(uint8_t* dest, uint32_t address, uint8_t size) { + uint32_t addressBank = address & 0xFFFFFF00; + uint32_t addressOffset = address & 0x000000FF; + auto it = spi_flash_data.find(addressBank); + + if (it != spi_flash_data.end()) { + const uint8_t* data = it->second; + memcpy(dest, data + addressOffset, size); + } else { + memset(dest, 0xFF, size); + } +} + +static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) { + bool canSend = false; + + switch (switchReportSubID) { + case IDENTIFY: + send_identify(); + canSend = true; + break; + case HANDSHAKE: + report_buffer[0] = REPORT_USB_INPUT_81; + report_buffer[1] = HANDSHAKE; + canSend = true; + break; + case BAUD_RATE: + report_buffer[0] = REPORT_USB_INPUT_81; + report_buffer[1] = BAUD_RATE; + canSend = true; + break; + case DISABLE_USB_TIMEOUT: + report_buffer[0] = REPORT_OUTPUT_30; + report_buffer[1] = switchReportSubID; + //if (handshakeCounter < 4) { + // handshakeCounter++; + //} else { + is_ready = true; + //} + canSend = true; + break; + case ENABLE_USB_TIMEOUT: + report_buffer[0] = REPORT_OUTPUT_30; + report_buffer[1] = switchReportSubID; + canSend = true; + break; + default: + report_buffer[0] = REPORT_OUTPUT_30; + report_buffer[1] = switchReportSubID; + canSend = true; + break; + } + + if (canSend) is_report_queued = true; +} + +static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) { + uint8_t commandID = reportData[10]; + uint32_t spiReadAddress = 0; + uint8_t spiReadSize = 0; + bool canSend = false; + + report_buffer[0] = REPORT_OUTPUT_21; + report_buffer[1] = last_report_counter; + memcpy(report_buffer + 2, &switch_report.inputs, sizeof(SwitchInputReport)); + + switch (commandID) { + case GET_CONTROLLER_STATE: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + report_buffer[15] = 0x03; + canSend = true; + break; + case BLUETOOTH_PAIR_REQUEST: + report_buffer[13] = 0x81; + report_buffer[14] = commandID; + report_buffer[15] = 0x03; + canSend = true; + break; + case REQUEST_DEVICE_INFO: + report_buffer[13] = 0x82; + report_buffer[14] = 0x02; + memcpy(&report_buffer[15], &device_info, sizeof(device_info)); + canSend = true; + break; + case SET_MODE: + input_mode = reportData[11]; + report_buffer[13] = 0x80; + report_buffer[14] = 0x03; + report_buffer[15] = input_mode; + canSend = true; + break; + case TRIGGER_BUTTONS: + report_buffer[13] = 0x83; + report_buffer[14] = 0x04; + canSend = true; + break; + case SET_SHIPMENT: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + canSend = true; + break; + case SPI_READ: + spiReadAddress = (reportData[14] << 24) | (reportData[13] << 16) | (reportData[12] << 8) | (reportData[11]); + spiReadSize = reportData[15]; + report_buffer[13] = 0x90; + report_buffer[14] = reportData[10]; + report_buffer[15] = reportData[11]; + report_buffer[16] = reportData[12]; + report_buffer[17] = reportData[13]; + report_buffer[18] = reportData[14]; + report_buffer[19] = reportData[15]; + read_spi_flash(&report_buffer[20], spiReadAddress, spiReadSize); + canSend = true; + break; + case SET_NFC_IR_CONFIG: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + canSend = true; + break; + case SET_NFC_IR_STATE: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + canSend = true; + break; + case SET_PLAYER_LIGHTS: + player_id = reportData[11]; + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + canSend = true; + break; + case GET_PLAYER_LIGHTS: + player_id = reportData[11]; + report_buffer[13] = 0xB0; + report_buffer[14] = commandID; + report_buffer[15] = player_id; + canSend = true; + break; + case COMMAND_UNKNOWN_33: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + report_buffer[15] = 0x03; + canSend = true; + break; + case SET_HOME_LIGHT: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + report_buffer[15] = 0x00; + canSend = true; + break; + case TOGGLE_IMU: + is_imu_enabled = reportData[11]; + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + report_buffer[15] = 0x00; + canSend = true; + break; + case IMU_SENSITIVITY: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + canSend = true; + break; + case ENABLE_VIBRATION: + is_vibration_enabled = reportData[11]; + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + report_buffer[15] = 0x00; + canSend = true; + break; + case READ_IMU: + report_buffer[13] = 0xC0; + report_buffer[14] = commandID; + report_buffer[15] = reportData[11]; + report_buffer[16] = reportData[12]; + canSend = true; + break; + case GET_VOLTAGE: + report_buffer[13] = 0xD0; + report_buffer[14] = 0x50; + report_buffer[15] = 0x83; + report_buffer[16] = 0x06; + canSend = true; + break; + default: + report_buffer[13] = 0x80; + report_buffer[14] = commandID; + report_buffer[15] = 0x03; + canSend = true; + break; + } + + if (canSend) is_report_queued = true; +} + +static void update_switch_report_from_state() { + switch_report.inputs.dpadUp = g_input_state.dpad_up; + switch_report.inputs.dpadDown = g_input_state.dpad_down; + switch_report.inputs.dpadLeft = g_input_state.dpad_left; + switch_report.inputs.dpadRight = g_input_state.dpad_right; + + switch_report.inputs.chargingGrip = 1; + + switch_report.inputs.buttonY = g_input_state.button_y; + switch_report.inputs.buttonX = g_input_state.button_x; + switch_report.inputs.buttonB = g_input_state.button_b; + switch_report.inputs.buttonA = g_input_state.button_a; + switch_report.inputs.buttonRightSR = 0; + switch_report.inputs.buttonRightSL = 0; + switch_report.inputs.buttonR = g_input_state.button_r; + switch_report.inputs.buttonZR = g_input_state.button_zr; + switch_report.inputs.buttonMinus = g_input_state.button_minus; + switch_report.inputs.buttonPlus = g_input_state.button_plus; + switch_report.inputs.buttonThumbR = g_input_state.button_r3; + switch_report.inputs.buttonThumbL = g_input_state.button_l3; + switch_report.inputs.buttonHome = g_input_state.button_home; + switch_report.inputs.buttonCapture = g_input_state.button_capture; + switch_report.inputs.buttonLeftSR = 0; + switch_report.inputs.buttonLeftSL = 0; + switch_report.inputs.buttonL = g_input_state.button_l; + switch_report.inputs.buttonZL = g_input_state.button_zl; + + uint16_t scaleLeftStickX = scale16To12(g_input_state.lx); + uint16_t scaleLeftStickY = scale16To12(g_input_state.ly); + uint16_t scaleRightStickX = scale16To12(g_input_state.rx); + uint16_t scaleRightStickY = scale16To12(g_input_state.ry); + + switch_report.inputs.leftStick.setX(std::min(std::max(scaleLeftStickX,leftMinX), leftMaxX)); + switch_report.inputs.leftStick.setY(-std::min(std::max(scaleLeftStickY,leftMinY), leftMaxY)); + switch_report.inputs.rightStick.setX(std::min(std::max(scaleRightStickX,rightMinX), rightMaxX)); + switch_report.inputs.rightStick.setY(-std::min(std::max(scaleRightStickY,rightMinY), rightMaxY)); + + switch_report.rumbleReport = 0x09; +} + +void switch_pro_init() { + player_id = 0; + last_report_counter = 0; + handshake_counter = 0; + is_ready = false; + is_initialized = false; + is_report_queued = false; + report_sent = false; + + device_info = { + .majorVersion = 0x04, + .minorVersion = 0x91, + .controllerType = SWITCH_TYPE_PRO_CONTROLLER, + .unknown00 = 0x02, + .macAddress = {0x7c, 0xbb, 0x8a, static_cast(get_rand_32() % 0xff), static_cast(get_rand_32() % 0xff), static_cast(get_rand_32() % 0xff)}, + .unknown01 = 0x01, + .storedColors = 0x02, + }; + + switch_report = { + .reportID = 0x30, + .timestamp = 0, + + .inputs { + .connectionInfo = 0, + .batteryLevel = 0x08, + + .buttonY = 0, + .buttonX = 0, + .buttonB = 0, + .buttonA = 0, + .buttonRightSR = 0, + .buttonRightSL = 0, + .buttonR = 0, + .buttonZR = 0, + + .buttonMinus = 0, + .buttonPlus = 0, + .buttonThumbR = 0, + .buttonThumbL = 0, + .buttonHome = 0, + .buttonCapture = 0, + .dummy = 0, + .chargingGrip = 0, + + .dpadDown = 0, + .dpadUp = 0, + .dpadRight = 0, + .dpadLeft = 0, + .buttonLeftSL = 0, + .buttonLeftSR = 0, + .buttonL = 0, + .buttonZL = 0, + .leftStick = {0xFF, 0xF7, 0x7F}, + .rightStick = {0xFF, 0xF7, 0x7F}, + }, + .rumbleReport = 0, + .imuData = {0x00}, + .padding = {0x00} + }; + + last_report_timer = to_ms_since_boot(get_absolute_time()); + + factory_config->leftStickCalibration.getRealMin(leftMinX, leftMinY); + factory_config->leftStickCalibration.getCenter(leftCenX, leftCenY); + factory_config->leftStickCalibration.getRealMax(leftMaxX, leftMaxY); + factory_config->rightStickCalibration.getRealMin(rightMinX, rightMinY); + factory_config->rightStickCalibration.getCenter(rightCenX, rightCenY); + factory_config->rightStickCalibration.getRealMax(rightMaxX, rightMaxY); +} + +void switch_pro_set_input(const SwitchInputState& state) { + g_input_state = state; +} + +void switch_pro_task() { + uint32_t now = to_ms_since_boot(get_absolute_time()); + report_sent = false; + + update_switch_report_from_state(); + + if (tud_suspended()) { + tud_remote_wakeup(); + } + + if (is_report_queued) { + if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) { + if (tud_hid_ready() && send_report(queued_report_id, report_buffer, 64) == true ) { + } + is_report_queued = false; + last_report_timer = now; + } + report_sent = true; + } + + if (is_ready && !report_sent) { + if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) { + switch_report.timestamp = last_report_counter; + void * inputReport = &switch_report; + uint16_t report_size = sizeof(switch_report); + if (memcmp(last_report, inputReport, report_size) != 0) { + if (tud_hid_ready() && send_report(0, inputReport, report_size) == true ) { + memcpy(last_report, inputReport, report_size); + report_sent = true; + } + + last_report_timer = now; + } + } + } else { + if (!is_initialized) { + send_identify(); + if (tud_hid_ready() && tud_hid_report(0, report_buffer, 64) == true) { + is_initialized = true; + report_sent = true; + } + + last_report_timer = now; + } + } +} + +bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length) { + // Packet format: 0xAA, buttons(2 LE), hat, lx, ly, rx, ry + if (length < 8 || packet[0] != 0xAA) { + return false; + } + + SwitchProOutReport out{}; + out.buttons = static_cast(packet[1]) | (static_cast(packet[2]) << 8); + out.hat = packet[3]; + out.lx = packet[4]; + out.ly = packet[5]; + out.rx = packet[6]; + out.ry = packet[7]; + + auto expand_axis = [](uint8_t v) -> uint16_t { + return static_cast(v) << 8 | v; + }; + + SwitchInputState state = make_neutral_state(); + + switch (out.hat) { + case SWITCH_PRO_HAT_UP: state.dpad_up = true; break; + case SWITCH_PRO_HAT_UPRIGHT: state.dpad_up = true; state.dpad_right = true; break; + case SWITCH_PRO_HAT_RIGHT: state.dpad_right = true; break; + case SWITCH_PRO_HAT_DOWNRIGHT: state.dpad_down = true; state.dpad_right = true; break; + case SWITCH_PRO_HAT_DOWN: state.dpad_down = true; break; + case SWITCH_PRO_HAT_DOWNLEFT: state.dpad_down = true; state.dpad_left = true; break; + case SWITCH_PRO_HAT_LEFT: state.dpad_left = true; break; + case SWITCH_PRO_HAT_UPLEFT: state.dpad_up = true; state.dpad_left = true; break; + default: break; + } + + state.button_y = out.buttons & SWITCH_PRO_MASK_Y; + state.button_x = out.buttons & SWITCH_PRO_MASK_X; + state.button_b = out.buttons & SWITCH_PRO_MASK_B; + state.button_a = out.buttons & SWITCH_PRO_MASK_A; + state.button_r = out.buttons & SWITCH_PRO_MASK_R; + state.button_zr = out.buttons & SWITCH_PRO_MASK_ZR; + state.button_plus = out.buttons & SWITCH_PRO_MASK_PLUS; + state.button_minus = out.buttons & SWITCH_PRO_MASK_MINUS; + state.button_r3 = out.buttons & SWITCH_PRO_MASK_R3; + state.button_l3 = out.buttons & SWITCH_PRO_MASK_L3; + state.button_home = out.buttons & SWITCH_PRO_MASK_HOME; + state.button_capture = out.buttons & SWITCH_PRO_MASK_CAPTURE; + state.button_zl = out.buttons & SWITCH_PRO_MASK_ZL; + state.button_l = out.buttons & SWITCH_PRO_MASK_L; + + state.lx = expand_axis(out.lx); + state.ly = expand_axis(out.ly); + state.rx = expand_axis(out.rx); + state.ry = expand_axis(out.ry); + + switch_pro_set_input(state); + return true; +} + +// HID callbacks +uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { + (void)instance; + (void)report_id; + (void)report_type; + (void)buffer; + (void)reqlen; + return 0; +} + +void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) { + (void)instance; + if (report_type != HID_REPORT_TYPE_OUTPUT) return; + + memset(report_buffer, 0x00, bufsize); + + uint8_t switchReportID = buffer[0]; + uint8_t switchReportSubID = buffer[1]; + if (switchReportID == REPORT_OUTPUT_00) { + } else if (switchReportID == REPORT_FEATURE) { + queued_report_id = report_id; + handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize); + } else if (switchReportID == REPORT_CONFIGURATION) { + queued_report_id = report_id; + handle_config_report(switchReportID, switchReportSubID, buffer, bufsize); + } else { + } +} + +uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) { + (void)itf; + return switch_pro_report_descriptor; +} + +uint8_t const * tud_descriptor_device_cb(void) { + return switch_pro_device_descriptor; +} + +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { + (void)index; + return switch_pro_configuration_descriptor; +} + +static uint16_t desc_str[32]; + +uint16_t const * tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void)langid; + + uint8_t chr_count; + + if ( index == 0 ) { + memcpy(&desc_str[1], switch_pro_string_language, 2); + chr_count = 1; + } else { + if ( index >= sizeof(switch_pro_string_descriptors)/sizeof(switch_pro_string_descriptors[0]) ) return nullptr; + + const uint8_t *str = switch_pro_string_descriptors[index]; + + chr_count = 0; + while ( str[chr_count] ) chr_count++; + if ( chr_count > 31 ) chr_count = 31; + + for(uint8_t i=0; i +#include +#include "switch_pro_descriptors.h" + +typedef struct { + bool dpad_up; + bool dpad_down; + bool dpad_left; + bool dpad_right; + + bool button_a; + bool button_b; + bool button_x; + bool button_y; + bool button_l; + bool button_r; + bool button_zl; + bool button_zr; + bool button_plus; + bool button_minus; + bool button_home; + bool button_capture; + bool button_l3; + bool button_r3; + + uint16_t lx; // 0-65535 + uint16_t ly; + uint16_t rx; + uint16_t ry; +} SwitchInputState; + +// Initialize USB state and calibration before entering the main loop. +void switch_pro_init(); + +// Update the desired controller state for the next USB report. +void switch_pro_set_input(const SwitchInputState& state); + +// Drive the Switch Pro USB state machine; call this frequently in the main loop. +void switch_pro_task(); + +// Convert a packed UART message into controller state (returns true if parsed). +bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length); diff --git a/tusb_config.h b/tusb_config.h new file mode 100644 index 0000000..ff97f2e --- /dev/null +++ b/tusb_config.h @@ -0,0 +1,38 @@ +// TinyUSB configuration tailored for a single Switch Pro style HID interface. +// Data is derived from TinyUSB examples and tuned for a 64-byte HID endpoint. +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED) +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4))) +#endif + +#define CFG_TUD_ENDPOINT0_SIZE 64 + +// Device class configuration +#define CFG_TUD_HID 1 +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +#define CFG_TUD_HID_EP_BUFSIZE 64 + +#ifdef __cplusplus +} +#endif + +#endif // _TUSB_CONFIG_H_ From 2225e70e5ed72ecb78993ee15fb95c2ad5fba77b Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 17:17:47 -0700 Subject: [PATCH 003/134] It's connecting!! --- CMakeLists.txt | 8 +- host_uart_logger.py | 33 ++++++++ switch-pico.cpp | 126 +++++++++++++++++++++++++++- switch_pro_descriptors.h | 12 +-- switch_pro_driver.cpp | 175 +++++++++++++++++++++++++++++++-------- switch_pro_driver.h | 9 +- tusb_config.h | 4 + 7 files changed, 322 insertions(+), 45 deletions(-) create mode 100644 host_uart_logger.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ff57c6..68779eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ if (EXISTS ${picoVscode}) include(${picoVscode}) endif() # ==================================================================================== +option(SWITCH_PICO_AUTOTEST "Auto-play grip-screen connection sequence on startup" OFF) set(PICO_BOARD pico CACHE STRING "Board type") # Pull in Raspberry Pi Pico SDK (must be before project) @@ -44,7 +45,8 @@ pico_set_program_name(switch-pico "switch-pico") pico_set_program_version(switch-pico "0.1") # Modify the below lines to enable/disable output over UART/USB -pico_enable_stdio_uart(switch-pico 0) +# UART0 is enabled for debug logging; USB stdio remains off. +pico_enable_stdio_uart(switch-pico 1) pico_enable_stdio_usb(switch-pico 0) # Add the standard library to the build @@ -56,6 +58,10 @@ target_link_libraries(switch-pico pico_rand ) +if (SWITCH_PICO_AUTOTEST) + target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_AUTOTEST=1) +endif() + # Add the standard include files to the build target_include_directories(switch-pico PRIVATE ${CMAKE_CURRENT_LIST_DIR} diff --git a/host_uart_logger.py b/host_uart_logger.py new file mode 100644 index 0000000..634db3a --- /dev/null +++ b/host_uart_logger.py @@ -0,0 +1,33 @@ +import argparse +import datetime +import sys + +try: + import serial +except ImportError: + print("pyserial not installed. Install with: pip install pyserial", file=sys.stderr) + sys.exit(1) + + +def main(): + parser = argparse.ArgumentParser(description="Read debug logs from Pico UART") + parser.add_argument("-p", "--port", required=True, help="Serial port (e.g. /dev/ttyUSB0 or COM3)") + parser.add_argument("-b", "--baud", type=int, default=115200, help="Baud rate (default: 115200)") + args = parser.parse_args() + + with serial.Serial(args.port, args.baud, timeout=1) as ser: + print(f"Opened {args.port} @ {args.baud}") + while True: + line = ser.readline() + if not line: + continue + ts = datetime.datetime.now().strftime("%H:%M:%S.%f")[:-3] + try: + text = line.decode(errors="replace").rstrip() + except Exception: + text = repr(line) + print(f"{ts} | {text}") + + +if __name__ == "__main__": + main() diff --git a/switch-pico.cpp b/switch-pico.cpp index 88f9fe6..70f1b21 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -11,6 +11,20 @@ #define UART_TX_PIN 4 #define UART_RX_PIN 5 +static bool g_last_mounted = false; +static bool g_last_ready = false; + +// Track the latest state provided by UART or the autopilot. +static SwitchInputState g_user_state; + +#ifdef SWITCH_PICO_AUTOTEST +static bool g_autopilot_active = true; +static uint32_t g_autopilot_counter = 0; +static absolute_time_t g_autopilot_last_tick = {0}; +static bool g_uart_activity = false; +static bool g_ready_logged = false; +#endif + static void init_uart_input() { uart_init(UART_ID, BAUD_RATE); gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); @@ -52,12 +66,104 @@ static void poll_uart_frames() { buffer[index++] = byte; if (index >= sizeof(buffer)) { - switch_pro_apply_uart_packet(buffer, sizeof(buffer)); + SwitchInputState parsed{}; + if (switch_pro_apply_uart_packet(buffer, sizeof(buffer), &parsed)) { + g_user_state = parsed; + printf("[UART] packet buttons=0x%04x hat=%u lx=%u ly=%u rx=%u ry=%u\n", + (parsed.button_a ? SWITCH_PRO_MASK_A : 0) | + (parsed.button_b ? SWITCH_PRO_MASK_B : 0) | + (parsed.button_x ? SWITCH_PRO_MASK_X : 0) | + (parsed.button_y ? SWITCH_PRO_MASK_Y : 0) | + (parsed.button_l ? SWITCH_PRO_MASK_L : 0) | + (parsed.button_r ? SWITCH_PRO_MASK_R : 0) | + (parsed.button_zl ? SWITCH_PRO_MASK_ZL : 0) | + (parsed.button_zr ? SWITCH_PRO_MASK_ZR : 0) | + (parsed.button_plus? SWITCH_PRO_MASK_PLUS: 0) | + (parsed.button_minus?SWITCH_PRO_MASK_MINUS:0) | + (parsed.button_home?SWITCH_PRO_MASK_HOME:0) | + (parsed.button_capture?SWITCH_PRO_MASK_CAPTURE:0) | + (parsed.button_l3 ? SWITCH_PRO_MASK_L3 : 0) | + (parsed.button_r3 ? SWITCH_PRO_MASK_R3 : 0), + parsed.dpad_up ? SWITCH_PRO_HAT_UP : + parsed.dpad_down ? SWITCH_PRO_HAT_DOWN : + parsed.dpad_left ? SWITCH_PRO_HAT_LEFT : + parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING, + parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8); + } +#ifdef SWITCH_PICO_AUTOTEST + g_uart_activity = true; +#endif index = 0; } } } +#ifdef SWITCH_PICO_AUTOTEST +// Replays the Switch-Fightstick grip-screen sequence: press L+R twice, then A twice. +static SwitchInputState autopilot_state(const SwitchInputState& fallback) { + if (!g_autopilot_active || g_uart_activity) { + g_autopilot_active = false; + return fallback; + } + + if (!tud_mounted()) { + g_autopilot_counter = 0; + g_autopilot_last_tick = {0}; + return fallback; + } + + absolute_time_t now = get_absolute_time(); + if (!to_us_since_boot(g_autopilot_last_tick)) { + g_autopilot_last_tick = now; + } + + // Run at ~1ms cadence similar to the LUFA fightstick timing. + if (absolute_time_diff_us(g_autopilot_last_tick, now) < 1000) { + return fallback; + } + g_autopilot_last_tick = now; + + SwitchInputState state = fallback; + state.lx = SWITCH_PRO_JOYSTICK_MID; + state.ly = SWITCH_PRO_JOYSTICK_MID; + state.rx = SWITCH_PRO_JOYSTICK_MID; + state.ry = SWITCH_PRO_JOYSTICK_MID; + + // Fire L+R twice then A twice, loop every ~300ms to keep trying. + uint32_t step = g_autopilot_counter % 300; + if (step == 25 || step == 50) { + state.button_l = true; + state.button_r = true; + } else if (step == 75 || step == 100) { + state.button_a = true; + } + + g_autopilot_counter++; + + return state; +} +#endif + +static void log_usb_state() { + bool mounted = tud_mounted(); + bool ready = switch_pro_is_ready(); + + if (mounted != g_last_mounted) { + g_last_mounted = mounted; + printf("[USB] %s\n", mounted ? "mounted" : "unmounted"); + } + if (ready != g_last_ready) { + g_last_ready = ready; + printf("[SWITCH] driver %s\n", ready ? "ready (handshake OK)" : "not ready"); + } +#ifdef SWITCH_PICO_AUTOTEST + if (ready && !g_ready_logged) { + g_ready_logged = true; + printf("[AUTO] ready -> autopilot active=%s\n", g_autopilot_active ? "true" : "false"); + } +#endif +} + int main() { board_init(); stdio_init_all(); @@ -66,11 +172,27 @@ int main() { tusb_init(); switch_pro_init(); - switch_pro_set_input(neutral_input()); + g_user_state = neutral_input(); + switch_pro_set_input(g_user_state); + + printf("[BOOT] switch-pico starting (UART0 log @ 115200)\n"); + printf("[INFO] AUTOTEST=%s UART1 pins TX=%d RX=%d baud=%d\n", + #ifdef SWITCH_PICO_AUTOTEST + "ON" + #else + "OFF" + #endif + , UART_TX_PIN, UART_RX_PIN, BAUD_RATE); while (true) { tud_task(); // USB device tasks poll_uart_frames(); // Pull controller state from UART1 + SwitchInputState state = g_user_state; +#ifdef SWITCH_PICO_AUTOTEST + state = autopilot_state(state); +#endif + switch_pro_set_input(state); switch_pro_task(); // Push state to the Switch host + log_usb_state(); } } diff --git a/switch_pro_descriptors.h b/switch_pro_descriptors.h index be1a667..01d0031 100644 --- a/switch_pro_descriptors.h +++ b/switch_pro_descriptors.h @@ -322,8 +322,10 @@ typedef struct } SwitchProOutReport; static const uint8_t switch_pro_string_language[] = { 0x09, 0x04 }; -static const uint8_t switch_pro_string_manufacturer[] = "Open Stick Community"; -static const uint8_t switch_pro_string_product[] = "GP2040-CE (Pro Controller)"; +static const uint8_t switch_pro_string_manufacturer[] = "Nintendo Co., Ltd."; +static const uint8_t switch_pro_string_product[] = "Pro Controller"; +// static const uint8_t switch_pro_string_manufacturer[] = "Open Stick Community"; +// static const uint8_t switch_pro_string_product[] = "GP2040-CE (Pro Controller)"; static const uint8_t switch_pro_string_version[] = "000000000001"; static const uint8_t *switch_pro_string_descriptors[] __attribute__((unused)) = @@ -340,8 +342,8 @@ static const uint8_t switch_pro_device_descriptor[] = 0x01, // bDescriptorType (Device) 0x00, 0x02, // bcdUSB 2.00 0x00, // bDeviceClass (Use class information in the Interface Descriptors) - 0x00, // bDeviceSubClass - 0x00, // bDeviceProtocol + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol 0x40, // bMaxPacketSize0 64 0x7E, 0x05, // idVendor 0x057E 0x09, 0x20, // idProduct 0x2009 @@ -394,7 +396,7 @@ static const uint8_t switch_pro_configuration_descriptor[] = 0x07, // bLength 0x05, // bDescriptorType (Endpoint) - 0x05, // bEndpointAddress (IN/D2H) + 0x81, // bEndpointAddress (IN/D2H) 0x03, // bmAttributes (Interrupt) 0x40, 0x00, // wMaxPacketSize 64 0x08, // bInterval 8 (unit depends on device speed) diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index f2e5df2..97158c1 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -22,11 +22,14 @@ static uint8_t last_report[SWITCH_PRO_ENDPOINT_SIZE] = {}; static SwitchProReport switch_report{}; static uint8_t last_report_counter = 0; static uint32_t last_report_timer = 0; +static uint32_t last_host_activity_ms = 0; static bool is_ready = false; static bool is_initialized = false; static bool is_report_queued = false; static bool report_sent = false; static uint8_t queued_report_id = 0; +static bool forced_ready = false; +static bool host_sent_out = false; static uint8_t handshake_counter = 0; static SwitchDeviceInfo device_info{}; @@ -47,31 +50,31 @@ static const uint8_t factory_config_data[0xEFF] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, + 0xFF, 0xFF, // device type - SWITCH_TYPE_PRO_CONTROLLER, + SWITCH_TYPE_PRO_CONTROLLER, // unknown - 0xA0, + 0xA0, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // color options 0x02, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, // config & calibration 1 0xE3, 0xFF, 0x39, 0xFF, 0xED, 0x01, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x09, 0x00, 0xEA, 0xFF, 0xA1, 0xFF, 0x3B, 0x34, 0x3B, 0x34, 0x3B, 0x34, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // config & calibration 2 // left stick - 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, 0xa4, 0x46, + 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, 0xa4, 0x46, 0x6a, // right stick @@ -92,32 +95,32 @@ static const uint8_t factory_config_data[0xEFF] = { // right grip color 0xEC, 0x00, 0x8C, - 0x01, + 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - - 0x50, 0xFD, 0x00, 0x00, 0xC6, 0x0F, - 0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14, - 0x54, 0x41, 0x15, 0x54, 0xC7, 0x79, 0x9C, 0x33, - 0x36, 0x63, - - 0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14, + + 0x50, 0xFD, 0x00, 0x00, 0xC6, 0x0F, + 0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14, + 0x54, 0x41, 0x15, 0x54, 0xC7, 0x79, 0x9C, 0x33, + 0x36, 0x63, + + 0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14, 0x54, 0x41, 0x15, 0x54, - + 0xC7, - 0x79, - - 0x9C, + 0x79, + + 0x9C, + + 0x33, + + 0x36, - 0x33, - - 0x36, - 0x63, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; @@ -125,9 +128,9 @@ static const uint8_t factory_config_data[0xEFF] = { static const uint8_t user_calibration_data[0x3F] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - + // Left Stick - 0xB2, 0xA1, 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, + 0xB2, 0xA1, 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, 0xa4, 0x46, 0x6a, // Right Stick @@ -176,6 +179,9 @@ static bool send_report(uint8_t reportID, const void* reportData, uint16_t repor } else { last_report_counter = 0; } + if (!result) { + printf("[HID] send_report failed id=%u len=%u\n", reportID, reportLength); + } return result; } @@ -194,21 +200,26 @@ static void read_spi_flash(uint8_t* dest, uint32_t address, uint8_t size) { static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) { bool canSend = false; + last_host_activity_ms = to_ms_since_boot(get_absolute_time()); + host_sent_out = true; switch (switchReportSubID) { case IDENTIFY: send_identify(); canSend = true; + printf("[HID] CONFIG IDENTIFY\n"); break; case HANDSHAKE: report_buffer[0] = REPORT_USB_INPUT_81; report_buffer[1] = HANDSHAKE; canSend = true; + printf("[HID] CONFIG HANDSHAKE\n"); break; case BAUD_RATE: report_buffer[0] = REPORT_USB_INPUT_81; report_buffer[1] = BAUD_RATE; canSend = true; + printf("[HID] CONFIG BAUD_RATE\n"); break; case DISABLE_USB_TIMEOUT: report_buffer[0] = REPORT_OUTPUT_30; @@ -219,16 +230,19 @@ static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSub is_ready = true; //} canSend = true; + printf("[HID] CONFIG DISABLE_USB_TIMEOUT -> ready\n"); break; case ENABLE_USB_TIMEOUT: report_buffer[0] = REPORT_OUTPUT_30; report_buffer[1] = switchReportSubID; canSend = true; + printf("[HID] CONFIG ENABLE_USB_TIMEOUT\n"); break; default: report_buffer[0] = REPORT_OUTPUT_30; report_buffer[1] = switchReportSubID; canSend = true; + printf("[HID] CONFIG unknown subid=0x%02x\n", switchReportSubID); break; } @@ -240,6 +254,8 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu uint32_t spiReadAddress = 0; uint8_t spiReadSize = 0; bool canSend = false; + last_host_activity_ms = to_ms_since_boot(get_absolute_time()); + host_sent_out = true; report_buffer[0] = REPORT_OUTPUT_21; report_buffer[1] = last_report_counter; @@ -251,18 +267,21 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; + printf("[HID] FEATURE GET_CONTROLLER_STATE\n"); break; case BLUETOOTH_PAIR_REQUEST: report_buffer[13] = 0x81; report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; + printf("[HID] FEATURE BLUETOOTH_PAIR_REQUEST\n"); break; case REQUEST_DEVICE_INFO: report_buffer[13] = 0x82; report_buffer[14] = 0x02; memcpy(&report_buffer[15], &device_info, sizeof(device_info)); canSend = true; + printf("[HID] FEATURE REQUEST_DEVICE_INFO\n"); break; case SET_MODE: input_mode = reportData[11]; @@ -270,16 +289,19 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = 0x03; report_buffer[15] = input_mode; canSend = true; + printf("[HID] FEATURE SET_MODE 0x%02x\n", input_mode); break; case TRIGGER_BUTTONS: report_buffer[13] = 0x83; report_buffer[14] = 0x04; canSend = true; + printf("[HID] FEATURE TRIGGER_BUTTONS\n"); break; case SET_SHIPMENT: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; + printf("[HID] FEATURE SET_SHIPMENT\n"); break; case SPI_READ: spiReadAddress = (reportData[14] << 24) | (reportData[13] << 16) | (reportData[12] << 8) | (reportData[11]); @@ -293,22 +315,26 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[19] = reportData[15]; read_spi_flash(&report_buffer[20], spiReadAddress, spiReadSize); canSend = true; + printf("[HID] FEATURE SPI_READ addr=0x%08lx size=%u\n", (unsigned long)spiReadAddress, spiReadSize); break; case SET_NFC_IR_CONFIG: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; + printf("[HID] FEATURE SET_NFC_IR_CONFIG\n"); break; case SET_NFC_IR_STATE: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; + printf("[HID] FEATURE SET_NFC_IR_STATE\n"); break; case SET_PLAYER_LIGHTS: player_id = reportData[11]; report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; + printf("[HID] FEATURE SET_PLAYER_LIGHTS player=%u\n", player_id); break; case GET_PLAYER_LIGHTS: player_id = reportData[11]; @@ -316,18 +342,21 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = player_id; canSend = true; + printf("[HID] FEATURE GET_PLAYER_LIGHTS player=%u\n", player_id); break; case COMMAND_UNKNOWN_33: report_buffer[13] = 0x80; report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; + printf("[HID] FEATURE COMMAND_UNKNOWN_33\n"); break; case SET_HOME_LIGHT: report_buffer[13] = 0x80; report_buffer[14] = commandID; report_buffer[15] = 0x00; canSend = true; + printf("[HID] FEATURE SET_HOME_LIGHT\n"); break; case TOGGLE_IMU: is_imu_enabled = reportData[11]; @@ -335,11 +364,13 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = 0x00; canSend = true; + printf("[HID] FEATURE TOGGLE_IMU %u\n", is_imu_enabled); break; case IMU_SENSITIVITY: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; + printf("[HID] FEATURE IMU_SENSITIVITY\n"); break; case ENABLE_VIBRATION: is_vibration_enabled = reportData[11]; @@ -347,6 +378,7 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = 0x00; canSend = true; + printf("[HID] FEATURE ENABLE_VIBRATION %u\n", is_vibration_enabled); break; case READ_IMU: report_buffer[13] = 0xC0; @@ -354,6 +386,7 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[15] = reportData[11]; report_buffer[16] = reportData[12]; canSend = true; + printf("[HID] FEATURE READ_IMU addr=%u size=%u\n", reportData[11], reportData[12]); break; case GET_VOLTAGE: report_buffer[13] = 0xD0; @@ -361,12 +394,14 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[15] = 0x83; report_buffer[16] = 0x06; canSend = true; + printf("[HID] FEATURE GET_VOLTAGE\n"); break; default: report_buffer[13] = 0x80; report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; + printf("[HID] FEATURE unknown cmd=0x%02x\n", commandID); break; } @@ -404,7 +439,7 @@ static void update_switch_report_from_state() { uint16_t scaleLeftStickY = scale16To12(g_input_state.ly); uint16_t scaleRightStickX = scale16To12(g_input_state.rx); uint16_t scaleRightStickY = scale16To12(g_input_state.ry); - + switch_report.inputs.leftStick.setX(std::min(std::max(scaleLeftStickX,leftMinX), leftMaxX)); switch_report.inputs.leftStick.setY(-std::min(std::max(scaleLeftStickY,leftMinY), leftMaxY)); switch_report.inputs.rightStick.setX(std::min(std::max(scaleRightStickX,rightMinX), rightMaxX)); @@ -421,6 +456,8 @@ void switch_pro_init() { is_initialized = false; is_report_queued = false; report_sent = false; + forced_ready = false; + host_sent_out = false; device_info = { .majorVersion = 0x04, @@ -437,8 +474,8 @@ void switch_pro_init() { .timestamp = 0, .inputs { - .connectionInfo = 0, - .batteryLevel = 0x08, + .connectionInfo = 0x08, // wired connection + .batteryLevel = 0x0F, // full battery .buttonY = 0, .buttonX = 0, @@ -475,6 +512,7 @@ void switch_pro_init() { }; last_report_timer = to_ms_since_boot(get_absolute_time()); + last_host_activity_ms = last_report_timer; factory_config->leftStickCalibration.getRealMin(leftMinX, leftMinY); factory_config->leftStickCalibration.getCenter(leftCenX, leftCenY); @@ -508,7 +546,8 @@ void switch_pro_task() { report_sent = true; } - if (is_ready && !report_sent) { + // If the host never sends feature/config reports (seen on Switch), force readiness after a timeout. + if (is_ready && !report_sent && host_sent_out) { if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) { switch_report.timestamp = last_report_counter; void * inputReport = &switch_report; @@ -535,7 +574,7 @@ void switch_pro_task() { } } -bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length) { +bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchInputState* out_state) { // Packet format: 0xAA, buttons(2 LE), hat, lx, ly, rx, ry if (length < 8 || packet[0] != 0xAA) { return false; @@ -587,18 +626,33 @@ bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length) { state.rx = expand_axis(out.rx); state.ry = expand_axis(out.ry); - switch_pro_set_input(state); + if (out_state) { + *out_state = state; + } else { + switch_pro_set_input(state); + } return true; } +bool switch_pro_is_ready() { + return is_ready; +} + +void switch_pro_mark_host_active() { + host_sent_out = true; +} + // HID callbacks uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { (void)instance; - (void)report_id; - (void)report_type; - (void)buffer; - (void)reqlen; - return 0; + printf("[HID] get_report id=%u type=%u len=%u\n", report_id, report_type, reqlen); + if (!buffer) return 0; + + // Serve the current input report for any GET_REPORT request. + uint16_t report_size = sizeof(switch_report); + if (reqlen < report_size) report_size = reqlen; + memcpy(buffer, &switch_report, report_size); + return report_size; } void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) { @@ -609,7 +663,12 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ uint8_t switchReportID = buffer[0]; uint8_t switchReportSubID = buffer[1]; + printf("[HID] set_report type=%d id=%u switchRID=0x%02x sub=0x%02x len=%u\n", + report_type, report_id, switchReportID, switchReportSubID, bufsize); + host_sent_out = true; if (switchReportID == REPORT_OUTPUT_00) { + // No-op, just acknowledge to clear any stalls. + return; } else if (switchReportID == REPORT_FEATURE) { queued_report_id = report_id; handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize); @@ -620,6 +679,26 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ } } +void tud_hid_report_received_cb(uint8_t instance, uint8_t report_id, uint8_t const* buffer, uint16_t bufsize) { + (void)instance; + // Host sent data on interrupt OUT; mirror the control path handling. + memset(report_buffer, 0x00, bufsize); + uint8_t switchReportID = buffer[0]; + uint8_t switchReportSubID = buffer[1]; + printf("[HID] report_received id=%u switchRID=0x%02x sub=0x%02x len=%u\n", + report_id, switchReportID, switchReportSubID, bufsize); + host_sent_out = true; + if (switchReportID == REPORT_OUTPUT_00) { + return; + } else if (switchReportID == REPORT_FEATURE) { + queued_report_id = report_id; + handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize); + } else if (switchReportID == REPORT_CONFIGURATION) { + queued_report_id = report_id; + handle_config_report(switchReportID, switchReportSubID, buffer, bufsize); + } +} + uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) { (void)itf; return switch_pro_report_descriptor; @@ -634,6 +713,30 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { return switch_pro_configuration_descriptor; } +bool tud_control_request_cb(uint8_t rhport, tusb_control_request_t const * request) { + (void)rhport; + printf("[CTRL] bmReq=0x%02x bReq=0x%02x wValue=0x%04x wIndex=0x%04x wLen=%u\n", + request->bmRequestType, request->bRequest, request->wValue, request->wIndex, request->wLength); + return false; // let TinyUSB handle it normally +} + +void tud_mount_cb(void) { + printf("[USB] mount_cb\n"); + last_host_activity_ms = to_ms_since_boot(get_absolute_time()); + forced_ready = false; + is_ready = false; + is_initialized = false; + host_sent_out = false; +} + +void tud_umount_cb(void) { + printf("[USB] umount_cb\n"); + forced_ready = false; + is_ready = false; + is_initialized = false; + host_sent_out = false; +} + static uint16_t desc_str[32]; uint16_t const * tud_descriptor_string_cb(uint8_t index, uint16_t langid) { diff --git a/switch_pro_driver.h b/switch_pro_driver.h index 54a0c8e..7fb2dc4 100644 --- a/switch_pro_driver.h +++ b/switch_pro_driver.h @@ -47,4 +47,11 @@ void switch_pro_set_input(const SwitchInputState& state); void switch_pro_task(); // Convert a packed UART message into controller state (returns true if parsed). -bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length); +// If out_state is null the parsed state is written directly to the driver. +bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchInputState* out_state = nullptr); + +// Driver state helpers +bool switch_pro_is_ready(); + +// Mark that the host has sent any OUT traffic (allows starting IN reports). +void switch_pro_mark_host_active(); diff --git a/tusb_config.h b/tusb_config.h index ff97f2e..9f48992 100644 --- a/tusb_config.h +++ b/tusb_config.h @@ -28,6 +28,10 @@ extern "C" { #define CFG_TUD_MSC 0 #define CFG_TUD_MIDI 0 #define CFG_TUD_VENDOR 0 +#ifdef CFG_TUSB_DEBUG +#undef CFG_TUSB_DEBUG +#endif +#define CFG_TUSB_DEBUG 2 #define CFG_TUD_HID_EP_BUFSIZE 64 From 07b1689da1424b249c3fa596714a670b7b452711 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 17:17:47 -0700 Subject: [PATCH 004/134] It's connecting!! --- CMakeLists.txt | 8 +- host_uart_logger.py | 33 ++++++++ switch-pico.cpp | 126 +++++++++++++++++++++++++++- switch_pro_descriptors.h | 12 +-- switch_pro_driver.cpp | 175 +++++++++++++++++++++++++++++++-------- switch_pro_driver.h | 9 +- tusb_config.h | 4 + 7 files changed, 322 insertions(+), 45 deletions(-) create mode 100644 host_uart_logger.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ff57c6..68779eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ if (EXISTS ${picoVscode}) include(${picoVscode}) endif() # ==================================================================================== +option(SWITCH_PICO_AUTOTEST "Auto-play grip-screen connection sequence on startup" OFF) set(PICO_BOARD pico CACHE STRING "Board type") # Pull in Raspberry Pi Pico SDK (must be before project) @@ -44,7 +45,8 @@ pico_set_program_name(switch-pico "switch-pico") pico_set_program_version(switch-pico "0.1") # Modify the below lines to enable/disable output over UART/USB -pico_enable_stdio_uart(switch-pico 0) +# UART0 is enabled for debug logging; USB stdio remains off. +pico_enable_stdio_uart(switch-pico 1) pico_enable_stdio_usb(switch-pico 0) # Add the standard library to the build @@ -56,6 +58,10 @@ target_link_libraries(switch-pico pico_rand ) +if (SWITCH_PICO_AUTOTEST) + target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_AUTOTEST=1) +endif() + # Add the standard include files to the build target_include_directories(switch-pico PRIVATE ${CMAKE_CURRENT_LIST_DIR} diff --git a/host_uart_logger.py b/host_uart_logger.py new file mode 100644 index 0000000..634db3a --- /dev/null +++ b/host_uart_logger.py @@ -0,0 +1,33 @@ +import argparse +import datetime +import sys + +try: + import serial +except ImportError: + print("pyserial not installed. Install with: pip install pyserial", file=sys.stderr) + sys.exit(1) + + +def main(): + parser = argparse.ArgumentParser(description="Read debug logs from Pico UART") + parser.add_argument("-p", "--port", required=True, help="Serial port (e.g. /dev/ttyUSB0 or COM3)") + parser.add_argument("-b", "--baud", type=int, default=115200, help="Baud rate (default: 115200)") + args = parser.parse_args() + + with serial.Serial(args.port, args.baud, timeout=1) as ser: + print(f"Opened {args.port} @ {args.baud}") + while True: + line = ser.readline() + if not line: + continue + ts = datetime.datetime.now().strftime("%H:%M:%S.%f")[:-3] + try: + text = line.decode(errors="replace").rstrip() + except Exception: + text = repr(line) + print(f"{ts} | {text}") + + +if __name__ == "__main__": + main() diff --git a/switch-pico.cpp b/switch-pico.cpp index 88f9fe6..70f1b21 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -11,6 +11,20 @@ #define UART_TX_PIN 4 #define UART_RX_PIN 5 +static bool g_last_mounted = false; +static bool g_last_ready = false; + +// Track the latest state provided by UART or the autopilot. +static SwitchInputState g_user_state; + +#ifdef SWITCH_PICO_AUTOTEST +static bool g_autopilot_active = true; +static uint32_t g_autopilot_counter = 0; +static absolute_time_t g_autopilot_last_tick = {0}; +static bool g_uart_activity = false; +static bool g_ready_logged = false; +#endif + static void init_uart_input() { uart_init(UART_ID, BAUD_RATE); gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); @@ -52,12 +66,104 @@ static void poll_uart_frames() { buffer[index++] = byte; if (index >= sizeof(buffer)) { - switch_pro_apply_uart_packet(buffer, sizeof(buffer)); + SwitchInputState parsed{}; + if (switch_pro_apply_uart_packet(buffer, sizeof(buffer), &parsed)) { + g_user_state = parsed; + printf("[UART] packet buttons=0x%04x hat=%u lx=%u ly=%u rx=%u ry=%u\n", + (parsed.button_a ? SWITCH_PRO_MASK_A : 0) | + (parsed.button_b ? SWITCH_PRO_MASK_B : 0) | + (parsed.button_x ? SWITCH_PRO_MASK_X : 0) | + (parsed.button_y ? SWITCH_PRO_MASK_Y : 0) | + (parsed.button_l ? SWITCH_PRO_MASK_L : 0) | + (parsed.button_r ? SWITCH_PRO_MASK_R : 0) | + (parsed.button_zl ? SWITCH_PRO_MASK_ZL : 0) | + (parsed.button_zr ? SWITCH_PRO_MASK_ZR : 0) | + (parsed.button_plus? SWITCH_PRO_MASK_PLUS: 0) | + (parsed.button_minus?SWITCH_PRO_MASK_MINUS:0) | + (parsed.button_home?SWITCH_PRO_MASK_HOME:0) | + (parsed.button_capture?SWITCH_PRO_MASK_CAPTURE:0) | + (parsed.button_l3 ? SWITCH_PRO_MASK_L3 : 0) | + (parsed.button_r3 ? SWITCH_PRO_MASK_R3 : 0), + parsed.dpad_up ? SWITCH_PRO_HAT_UP : + parsed.dpad_down ? SWITCH_PRO_HAT_DOWN : + parsed.dpad_left ? SWITCH_PRO_HAT_LEFT : + parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING, + parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8); + } +#ifdef SWITCH_PICO_AUTOTEST + g_uart_activity = true; +#endif index = 0; } } } +#ifdef SWITCH_PICO_AUTOTEST +// Replays the Switch-Fightstick grip-screen sequence: press L+R twice, then A twice. +static SwitchInputState autopilot_state(const SwitchInputState& fallback) { + if (!g_autopilot_active || g_uart_activity) { + g_autopilot_active = false; + return fallback; + } + + if (!tud_mounted()) { + g_autopilot_counter = 0; + g_autopilot_last_tick = {0}; + return fallback; + } + + absolute_time_t now = get_absolute_time(); + if (!to_us_since_boot(g_autopilot_last_tick)) { + g_autopilot_last_tick = now; + } + + // Run at ~1ms cadence similar to the LUFA fightstick timing. + if (absolute_time_diff_us(g_autopilot_last_tick, now) < 1000) { + return fallback; + } + g_autopilot_last_tick = now; + + SwitchInputState state = fallback; + state.lx = SWITCH_PRO_JOYSTICK_MID; + state.ly = SWITCH_PRO_JOYSTICK_MID; + state.rx = SWITCH_PRO_JOYSTICK_MID; + state.ry = SWITCH_PRO_JOYSTICK_MID; + + // Fire L+R twice then A twice, loop every ~300ms to keep trying. + uint32_t step = g_autopilot_counter % 300; + if (step == 25 || step == 50) { + state.button_l = true; + state.button_r = true; + } else if (step == 75 || step == 100) { + state.button_a = true; + } + + g_autopilot_counter++; + + return state; +} +#endif + +static void log_usb_state() { + bool mounted = tud_mounted(); + bool ready = switch_pro_is_ready(); + + if (mounted != g_last_mounted) { + g_last_mounted = mounted; + printf("[USB] %s\n", mounted ? "mounted" : "unmounted"); + } + if (ready != g_last_ready) { + g_last_ready = ready; + printf("[SWITCH] driver %s\n", ready ? "ready (handshake OK)" : "not ready"); + } +#ifdef SWITCH_PICO_AUTOTEST + if (ready && !g_ready_logged) { + g_ready_logged = true; + printf("[AUTO] ready -> autopilot active=%s\n", g_autopilot_active ? "true" : "false"); + } +#endif +} + int main() { board_init(); stdio_init_all(); @@ -66,11 +172,27 @@ int main() { tusb_init(); switch_pro_init(); - switch_pro_set_input(neutral_input()); + g_user_state = neutral_input(); + switch_pro_set_input(g_user_state); + + printf("[BOOT] switch-pico starting (UART0 log @ 115200)\n"); + printf("[INFO] AUTOTEST=%s UART1 pins TX=%d RX=%d baud=%d\n", + #ifdef SWITCH_PICO_AUTOTEST + "ON" + #else + "OFF" + #endif + , UART_TX_PIN, UART_RX_PIN, BAUD_RATE); while (true) { tud_task(); // USB device tasks poll_uart_frames(); // Pull controller state from UART1 + SwitchInputState state = g_user_state; +#ifdef SWITCH_PICO_AUTOTEST + state = autopilot_state(state); +#endif + switch_pro_set_input(state); switch_pro_task(); // Push state to the Switch host + log_usb_state(); } } diff --git a/switch_pro_descriptors.h b/switch_pro_descriptors.h index be1a667..01d0031 100644 --- a/switch_pro_descriptors.h +++ b/switch_pro_descriptors.h @@ -322,8 +322,10 @@ typedef struct } SwitchProOutReport; static const uint8_t switch_pro_string_language[] = { 0x09, 0x04 }; -static const uint8_t switch_pro_string_manufacturer[] = "Open Stick Community"; -static const uint8_t switch_pro_string_product[] = "GP2040-CE (Pro Controller)"; +static const uint8_t switch_pro_string_manufacturer[] = "Nintendo Co., Ltd."; +static const uint8_t switch_pro_string_product[] = "Pro Controller"; +// static const uint8_t switch_pro_string_manufacturer[] = "Open Stick Community"; +// static const uint8_t switch_pro_string_product[] = "GP2040-CE (Pro Controller)"; static const uint8_t switch_pro_string_version[] = "000000000001"; static const uint8_t *switch_pro_string_descriptors[] __attribute__((unused)) = @@ -340,8 +342,8 @@ static const uint8_t switch_pro_device_descriptor[] = 0x01, // bDescriptorType (Device) 0x00, 0x02, // bcdUSB 2.00 0x00, // bDeviceClass (Use class information in the Interface Descriptors) - 0x00, // bDeviceSubClass - 0x00, // bDeviceProtocol + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol 0x40, // bMaxPacketSize0 64 0x7E, 0x05, // idVendor 0x057E 0x09, 0x20, // idProduct 0x2009 @@ -394,7 +396,7 @@ static const uint8_t switch_pro_configuration_descriptor[] = 0x07, // bLength 0x05, // bDescriptorType (Endpoint) - 0x05, // bEndpointAddress (IN/D2H) + 0x81, // bEndpointAddress (IN/D2H) 0x03, // bmAttributes (Interrupt) 0x40, 0x00, // wMaxPacketSize 64 0x08, // bInterval 8 (unit depends on device speed) diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index f2e5df2..97158c1 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -22,11 +22,14 @@ static uint8_t last_report[SWITCH_PRO_ENDPOINT_SIZE] = {}; static SwitchProReport switch_report{}; static uint8_t last_report_counter = 0; static uint32_t last_report_timer = 0; +static uint32_t last_host_activity_ms = 0; static bool is_ready = false; static bool is_initialized = false; static bool is_report_queued = false; static bool report_sent = false; static uint8_t queued_report_id = 0; +static bool forced_ready = false; +static bool host_sent_out = false; static uint8_t handshake_counter = 0; static SwitchDeviceInfo device_info{}; @@ -47,31 +50,31 @@ static const uint8_t factory_config_data[0xEFF] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, + 0xFF, 0xFF, // device type - SWITCH_TYPE_PRO_CONTROLLER, + SWITCH_TYPE_PRO_CONTROLLER, // unknown - 0xA0, + 0xA0, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // color options 0x02, - 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, // config & calibration 1 0xE3, 0xFF, 0x39, 0xFF, 0xED, 0x01, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x09, 0x00, 0xEA, 0xFF, 0xA1, 0xFF, 0x3B, 0x34, 0x3B, 0x34, 0x3B, 0x34, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // config & calibration 2 // left stick - 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, 0xa4, 0x46, + 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, 0xa4, 0x46, 0x6a, // right stick @@ -92,32 +95,32 @@ static const uint8_t factory_config_data[0xEFF] = { // right grip color 0xEC, 0x00, 0x8C, - 0x01, + 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - - 0x50, 0xFD, 0x00, 0x00, 0xC6, 0x0F, - 0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14, - 0x54, 0x41, 0x15, 0x54, 0xC7, 0x79, 0x9C, 0x33, - 0x36, 0x63, - - 0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14, + + 0x50, 0xFD, 0x00, 0x00, 0xC6, 0x0F, + 0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14, + 0x54, 0x41, 0x15, 0x54, 0xC7, 0x79, 0x9C, 0x33, + 0x36, 0x63, + + 0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14, 0x54, 0x41, 0x15, 0x54, - + 0xC7, - 0x79, - - 0x9C, + 0x79, + + 0x9C, + + 0x33, + + 0x36, - 0x33, - - 0x36, - 0x63, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; @@ -125,9 +128,9 @@ static const uint8_t factory_config_data[0xEFF] = { static const uint8_t user_calibration_data[0x3F] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - + // Left Stick - 0xB2, 0xA1, 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, + 0xB2, 0xA1, 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, 0xa4, 0x46, 0x6a, // Right Stick @@ -176,6 +179,9 @@ static bool send_report(uint8_t reportID, const void* reportData, uint16_t repor } else { last_report_counter = 0; } + if (!result) { + printf("[HID] send_report failed id=%u len=%u\n", reportID, reportLength); + } return result; } @@ -194,21 +200,26 @@ static void read_spi_flash(uint8_t* dest, uint32_t address, uint8_t size) { static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) { bool canSend = false; + last_host_activity_ms = to_ms_since_boot(get_absolute_time()); + host_sent_out = true; switch (switchReportSubID) { case IDENTIFY: send_identify(); canSend = true; + printf("[HID] CONFIG IDENTIFY\n"); break; case HANDSHAKE: report_buffer[0] = REPORT_USB_INPUT_81; report_buffer[1] = HANDSHAKE; canSend = true; + printf("[HID] CONFIG HANDSHAKE\n"); break; case BAUD_RATE: report_buffer[0] = REPORT_USB_INPUT_81; report_buffer[1] = BAUD_RATE; canSend = true; + printf("[HID] CONFIG BAUD_RATE\n"); break; case DISABLE_USB_TIMEOUT: report_buffer[0] = REPORT_OUTPUT_30; @@ -219,16 +230,19 @@ static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSub is_ready = true; //} canSend = true; + printf("[HID] CONFIG DISABLE_USB_TIMEOUT -> ready\n"); break; case ENABLE_USB_TIMEOUT: report_buffer[0] = REPORT_OUTPUT_30; report_buffer[1] = switchReportSubID; canSend = true; + printf("[HID] CONFIG ENABLE_USB_TIMEOUT\n"); break; default: report_buffer[0] = REPORT_OUTPUT_30; report_buffer[1] = switchReportSubID; canSend = true; + printf("[HID] CONFIG unknown subid=0x%02x\n", switchReportSubID); break; } @@ -240,6 +254,8 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu uint32_t spiReadAddress = 0; uint8_t spiReadSize = 0; bool canSend = false; + last_host_activity_ms = to_ms_since_boot(get_absolute_time()); + host_sent_out = true; report_buffer[0] = REPORT_OUTPUT_21; report_buffer[1] = last_report_counter; @@ -251,18 +267,21 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; + printf("[HID] FEATURE GET_CONTROLLER_STATE\n"); break; case BLUETOOTH_PAIR_REQUEST: report_buffer[13] = 0x81; report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; + printf("[HID] FEATURE BLUETOOTH_PAIR_REQUEST\n"); break; case REQUEST_DEVICE_INFO: report_buffer[13] = 0x82; report_buffer[14] = 0x02; memcpy(&report_buffer[15], &device_info, sizeof(device_info)); canSend = true; + printf("[HID] FEATURE REQUEST_DEVICE_INFO\n"); break; case SET_MODE: input_mode = reportData[11]; @@ -270,16 +289,19 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = 0x03; report_buffer[15] = input_mode; canSend = true; + printf("[HID] FEATURE SET_MODE 0x%02x\n", input_mode); break; case TRIGGER_BUTTONS: report_buffer[13] = 0x83; report_buffer[14] = 0x04; canSend = true; + printf("[HID] FEATURE TRIGGER_BUTTONS\n"); break; case SET_SHIPMENT: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; + printf("[HID] FEATURE SET_SHIPMENT\n"); break; case SPI_READ: spiReadAddress = (reportData[14] << 24) | (reportData[13] << 16) | (reportData[12] << 8) | (reportData[11]); @@ -293,22 +315,26 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[19] = reportData[15]; read_spi_flash(&report_buffer[20], spiReadAddress, spiReadSize); canSend = true; + printf("[HID] FEATURE SPI_READ addr=0x%08lx size=%u\n", (unsigned long)spiReadAddress, spiReadSize); break; case SET_NFC_IR_CONFIG: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; + printf("[HID] FEATURE SET_NFC_IR_CONFIG\n"); break; case SET_NFC_IR_STATE: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; + printf("[HID] FEATURE SET_NFC_IR_STATE\n"); break; case SET_PLAYER_LIGHTS: player_id = reportData[11]; report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; + printf("[HID] FEATURE SET_PLAYER_LIGHTS player=%u\n", player_id); break; case GET_PLAYER_LIGHTS: player_id = reportData[11]; @@ -316,18 +342,21 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = player_id; canSend = true; + printf("[HID] FEATURE GET_PLAYER_LIGHTS player=%u\n", player_id); break; case COMMAND_UNKNOWN_33: report_buffer[13] = 0x80; report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; + printf("[HID] FEATURE COMMAND_UNKNOWN_33\n"); break; case SET_HOME_LIGHT: report_buffer[13] = 0x80; report_buffer[14] = commandID; report_buffer[15] = 0x00; canSend = true; + printf("[HID] FEATURE SET_HOME_LIGHT\n"); break; case TOGGLE_IMU: is_imu_enabled = reportData[11]; @@ -335,11 +364,13 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = 0x00; canSend = true; + printf("[HID] FEATURE TOGGLE_IMU %u\n", is_imu_enabled); break; case IMU_SENSITIVITY: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; + printf("[HID] FEATURE IMU_SENSITIVITY\n"); break; case ENABLE_VIBRATION: is_vibration_enabled = reportData[11]; @@ -347,6 +378,7 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = 0x00; canSend = true; + printf("[HID] FEATURE ENABLE_VIBRATION %u\n", is_vibration_enabled); break; case READ_IMU: report_buffer[13] = 0xC0; @@ -354,6 +386,7 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[15] = reportData[11]; report_buffer[16] = reportData[12]; canSend = true; + printf("[HID] FEATURE READ_IMU addr=%u size=%u\n", reportData[11], reportData[12]); break; case GET_VOLTAGE: report_buffer[13] = 0xD0; @@ -361,12 +394,14 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[15] = 0x83; report_buffer[16] = 0x06; canSend = true; + printf("[HID] FEATURE GET_VOLTAGE\n"); break; default: report_buffer[13] = 0x80; report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; + printf("[HID] FEATURE unknown cmd=0x%02x\n", commandID); break; } @@ -404,7 +439,7 @@ static void update_switch_report_from_state() { uint16_t scaleLeftStickY = scale16To12(g_input_state.ly); uint16_t scaleRightStickX = scale16To12(g_input_state.rx); uint16_t scaleRightStickY = scale16To12(g_input_state.ry); - + switch_report.inputs.leftStick.setX(std::min(std::max(scaleLeftStickX,leftMinX), leftMaxX)); switch_report.inputs.leftStick.setY(-std::min(std::max(scaleLeftStickY,leftMinY), leftMaxY)); switch_report.inputs.rightStick.setX(std::min(std::max(scaleRightStickX,rightMinX), rightMaxX)); @@ -421,6 +456,8 @@ void switch_pro_init() { is_initialized = false; is_report_queued = false; report_sent = false; + forced_ready = false; + host_sent_out = false; device_info = { .majorVersion = 0x04, @@ -437,8 +474,8 @@ void switch_pro_init() { .timestamp = 0, .inputs { - .connectionInfo = 0, - .batteryLevel = 0x08, + .connectionInfo = 0x08, // wired connection + .batteryLevel = 0x0F, // full battery .buttonY = 0, .buttonX = 0, @@ -475,6 +512,7 @@ void switch_pro_init() { }; last_report_timer = to_ms_since_boot(get_absolute_time()); + last_host_activity_ms = last_report_timer; factory_config->leftStickCalibration.getRealMin(leftMinX, leftMinY); factory_config->leftStickCalibration.getCenter(leftCenX, leftCenY); @@ -508,7 +546,8 @@ void switch_pro_task() { report_sent = true; } - if (is_ready && !report_sent) { + // If the host never sends feature/config reports (seen on Switch), force readiness after a timeout. + if (is_ready && !report_sent && host_sent_out) { if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) { switch_report.timestamp = last_report_counter; void * inputReport = &switch_report; @@ -535,7 +574,7 @@ void switch_pro_task() { } } -bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length) { +bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchInputState* out_state) { // Packet format: 0xAA, buttons(2 LE), hat, lx, ly, rx, ry if (length < 8 || packet[0] != 0xAA) { return false; @@ -587,18 +626,33 @@ bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length) { state.rx = expand_axis(out.rx); state.ry = expand_axis(out.ry); - switch_pro_set_input(state); + if (out_state) { + *out_state = state; + } else { + switch_pro_set_input(state); + } return true; } +bool switch_pro_is_ready() { + return is_ready; +} + +void switch_pro_mark_host_active() { + host_sent_out = true; +} + // HID callbacks uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { (void)instance; - (void)report_id; - (void)report_type; - (void)buffer; - (void)reqlen; - return 0; + printf("[HID] get_report id=%u type=%u len=%u\n", report_id, report_type, reqlen); + if (!buffer) return 0; + + // Serve the current input report for any GET_REPORT request. + uint16_t report_size = sizeof(switch_report); + if (reqlen < report_size) report_size = reqlen; + memcpy(buffer, &switch_report, report_size); + return report_size; } void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) { @@ -609,7 +663,12 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ uint8_t switchReportID = buffer[0]; uint8_t switchReportSubID = buffer[1]; + printf("[HID] set_report type=%d id=%u switchRID=0x%02x sub=0x%02x len=%u\n", + report_type, report_id, switchReportID, switchReportSubID, bufsize); + host_sent_out = true; if (switchReportID == REPORT_OUTPUT_00) { + // No-op, just acknowledge to clear any stalls. + return; } else if (switchReportID == REPORT_FEATURE) { queued_report_id = report_id; handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize); @@ -620,6 +679,26 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ } } +void tud_hid_report_received_cb(uint8_t instance, uint8_t report_id, uint8_t const* buffer, uint16_t bufsize) { + (void)instance; + // Host sent data on interrupt OUT; mirror the control path handling. + memset(report_buffer, 0x00, bufsize); + uint8_t switchReportID = buffer[0]; + uint8_t switchReportSubID = buffer[1]; + printf("[HID] report_received id=%u switchRID=0x%02x sub=0x%02x len=%u\n", + report_id, switchReportID, switchReportSubID, bufsize); + host_sent_out = true; + if (switchReportID == REPORT_OUTPUT_00) { + return; + } else if (switchReportID == REPORT_FEATURE) { + queued_report_id = report_id; + handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize); + } else if (switchReportID == REPORT_CONFIGURATION) { + queued_report_id = report_id; + handle_config_report(switchReportID, switchReportSubID, buffer, bufsize); + } +} + uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) { (void)itf; return switch_pro_report_descriptor; @@ -634,6 +713,30 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { return switch_pro_configuration_descriptor; } +bool tud_control_request_cb(uint8_t rhport, tusb_control_request_t const * request) { + (void)rhport; + printf("[CTRL] bmReq=0x%02x bReq=0x%02x wValue=0x%04x wIndex=0x%04x wLen=%u\n", + request->bmRequestType, request->bRequest, request->wValue, request->wIndex, request->wLength); + return false; // let TinyUSB handle it normally +} + +void tud_mount_cb(void) { + printf("[USB] mount_cb\n"); + last_host_activity_ms = to_ms_since_boot(get_absolute_time()); + forced_ready = false; + is_ready = false; + is_initialized = false; + host_sent_out = false; +} + +void tud_umount_cb(void) { + printf("[USB] umount_cb\n"); + forced_ready = false; + is_ready = false; + is_initialized = false; + host_sent_out = false; +} + static uint16_t desc_str[32]; uint16_t const * tud_descriptor_string_cb(uint8_t index, uint16_t langid) { diff --git a/switch_pro_driver.h b/switch_pro_driver.h index 54a0c8e..7fb2dc4 100644 --- a/switch_pro_driver.h +++ b/switch_pro_driver.h @@ -47,4 +47,11 @@ void switch_pro_set_input(const SwitchInputState& state); void switch_pro_task(); // Convert a packed UART message into controller state (returns true if parsed). -bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length); +// If out_state is null the parsed state is written directly to the driver. +bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchInputState* out_state = nullptr); + +// Driver state helpers +bool switch_pro_is_ready(); + +// Mark that the host has sent any OUT traffic (allows starting IN reports). +void switch_pro_mark_host_active(); diff --git a/tusb_config.h b/tusb_config.h index ff97f2e..9f48992 100644 --- a/tusb_config.h +++ b/tusb_config.h @@ -28,6 +28,10 @@ extern "C" { #define CFG_TUD_MSC 0 #define CFG_TUD_MIDI 0 #define CFG_TUD_VENDOR 0 +#ifdef CFG_TUSB_DEBUG +#undef CFG_TUSB_DEBUG +#endif +#define CFG_TUSB_DEBUG 2 #define CFG_TUD_HID_EP_BUFSIZE 64 From e7224c13d22a86094a995202c3ac96d4f6779aa8 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 17:39:53 -0700 Subject: [PATCH 005/134] Remove debug logging if flag is present --- .gitignore | 2 ++ CMakeLists.txt | 4 +++ host_uart_logger.py | 2 +- switch-pico.cpp | 58 ++++++++++++++++-------------- switch_pro_driver.cpp | 84 ++++++++++++++++++++++++------------------- tusb_config.h | 1 + 6 files changed, 88 insertions(+), 63 deletions(-) diff --git a/.gitignore b/.gitignore index 4ed84ef..d9e1db8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.cache +Switch-Fightstick GP2040-CE build !.vscode/* diff --git a/CMakeLists.txt b/CMakeLists.txt index 68779eb..896fe25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ if (EXISTS ${picoVscode}) endif() # ==================================================================================== option(SWITCH_PICO_AUTOTEST "Auto-play grip-screen connection sequence on startup" OFF) +option(SWITCH_PICO_LOG "Enable UART debug logging" OFF) set(PICO_BOARD pico CACHE STRING "Board type") # Pull in Raspberry Pi Pico SDK (must be before project) @@ -61,6 +62,9 @@ target_link_libraries(switch-pico if (SWITCH_PICO_AUTOTEST) target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_AUTOTEST=1) endif() +if (SWITCH_PICO_LOG) + target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_LOG=1) +endif() # Add the standard include files to the build target_include_directories(switch-pico PRIVATE diff --git a/host_uart_logger.py b/host_uart_logger.py index 634db3a..658f3ca 100644 --- a/host_uart_logger.py +++ b/host_uart_logger.py @@ -12,7 +12,7 @@ except ImportError: def main(): parser = argparse.ArgumentParser(description="Read debug logs from Pico UART") parser.add_argument("-p", "--port", required=True, help="Serial port (e.g. /dev/ttyUSB0 or COM3)") - parser.add_argument("-b", "--baud", type=int, default=115200, help="Baud rate (default: 115200)") + parser.add_argument("-b", "--baud", type=int, default=1000000, help="Baud rate (default: 1000000)") args = parser.parse_args() with serial.Serial(args.port, args.baud, timeout=1) as ser: diff --git a/switch-pico.cpp b/switch-pico.cpp index 70f1b21..1d403de 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -5,9 +5,15 @@ #include "tusb.h" #include "switch_pro_driver.h" +#ifdef SWITCH_PICO_LOG +#define LOG_PRINTF(...) printf(__VA_ARGS__) +#else +#define LOG_PRINTF(...) ((void)0) +#endif + // UART1 is reserved for external input frames from the host PC. #define UART_ID uart1 -#define BAUD_RATE 115200 +#define BAUD_RATE 1000000 #define UART_TX_PIN 4 #define UART_RX_PIN 5 @@ -69,26 +75,26 @@ static void poll_uart_frames() { SwitchInputState parsed{}; if (switch_pro_apply_uart_packet(buffer, sizeof(buffer), &parsed)) { g_user_state = parsed; - printf("[UART] packet buttons=0x%04x hat=%u lx=%u ly=%u rx=%u ry=%u\n", - (parsed.button_a ? SWITCH_PRO_MASK_A : 0) | - (parsed.button_b ? SWITCH_PRO_MASK_B : 0) | - (parsed.button_x ? SWITCH_PRO_MASK_X : 0) | - (parsed.button_y ? SWITCH_PRO_MASK_Y : 0) | - (parsed.button_l ? SWITCH_PRO_MASK_L : 0) | - (parsed.button_r ? SWITCH_PRO_MASK_R : 0) | - (parsed.button_zl ? SWITCH_PRO_MASK_ZL : 0) | - (parsed.button_zr ? SWITCH_PRO_MASK_ZR : 0) | - (parsed.button_plus? SWITCH_PRO_MASK_PLUS: 0) | - (parsed.button_minus?SWITCH_PRO_MASK_MINUS:0) | - (parsed.button_home?SWITCH_PRO_MASK_HOME:0) | - (parsed.button_capture?SWITCH_PRO_MASK_CAPTURE:0) | - (parsed.button_l3 ? SWITCH_PRO_MASK_L3 : 0) | - (parsed.button_r3 ? SWITCH_PRO_MASK_R3 : 0), - parsed.dpad_up ? SWITCH_PRO_HAT_UP : - parsed.dpad_down ? SWITCH_PRO_HAT_DOWN : - parsed.dpad_left ? SWITCH_PRO_HAT_LEFT : - parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING, - parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8); + LOG_PRINTF("[UART] packet buttons=0x%04x hat=%u lx=%u ly=%u rx=%u ry=%u\n", + (parsed.button_a ? SWITCH_PRO_MASK_A : 0) | + (parsed.button_b ? SWITCH_PRO_MASK_B : 0) | + (parsed.button_x ? SWITCH_PRO_MASK_X : 0) | + (parsed.button_y ? SWITCH_PRO_MASK_Y : 0) | + (parsed.button_l ? SWITCH_PRO_MASK_L : 0) | + (parsed.button_r ? SWITCH_PRO_MASK_R : 0) | + (parsed.button_zl ? SWITCH_PRO_MASK_ZL : 0) | + (parsed.button_zr ? SWITCH_PRO_MASK_ZR : 0) | + (parsed.button_plus? SWITCH_PRO_MASK_PLUS: 0) | + (parsed.button_minus?SWITCH_PRO_MASK_MINUS:0) | + (parsed.button_home?SWITCH_PRO_MASK_HOME:0) | + (parsed.button_capture?SWITCH_PRO_MASK_CAPTURE:0) | + (parsed.button_l3 ? SWITCH_PRO_MASK_L3 : 0) | + (parsed.button_r3 ? SWITCH_PRO_MASK_R3 : 0), + parsed.dpad_up ? SWITCH_PRO_HAT_UP : + parsed.dpad_down ? SWITCH_PRO_HAT_DOWN : + parsed.dpad_left ? SWITCH_PRO_HAT_LEFT : + parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING, + parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8); } #ifdef SWITCH_PICO_AUTOTEST g_uart_activity = true; @@ -150,16 +156,16 @@ static void log_usb_state() { if (mounted != g_last_mounted) { g_last_mounted = mounted; - printf("[USB] %s\n", mounted ? "mounted" : "unmounted"); + LOG_PRINTF("[USB] %s\n", mounted ? "mounted" : "unmounted"); } if (ready != g_last_ready) { g_last_ready = ready; - printf("[SWITCH] driver %s\n", ready ? "ready (handshake OK)" : "not ready"); + LOG_PRINTF("[SWITCH] driver %s\n", ready ? "ready (handshake OK)" : "not ready"); } #ifdef SWITCH_PICO_AUTOTEST if (ready && !g_ready_logged) { g_ready_logged = true; - printf("[AUTO] ready -> autopilot active=%s\n", g_autopilot_active ? "true" : "false"); + LOG_PRINTF("[AUTO] ready -> autopilot active=%s\n", g_autopilot_active ? "true" : "false"); } #endif } @@ -175,8 +181,8 @@ int main() { g_user_state = neutral_input(); switch_pro_set_input(g_user_state); - printf("[BOOT] switch-pico starting (UART0 log @ 115200)\n"); - printf("[INFO] AUTOTEST=%s UART1 pins TX=%d RX=%d baud=%d\n", + LOG_PRINTF("[BOOT] switch-pico starting (UART0 log @ 115200)\n"); + LOG_PRINTF("[INFO] AUTOTEST=%s UART1 pins TX=%d RX=%d baud=%d\n", #ifdef SWITCH_PICO_AUTOTEST "ON" #else diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index 97158c1..63765db 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -3,10 +3,17 @@ #include #include #include +#include #include "pico/rand.h" #include "pico/time.h" #include "tusb.h" +#ifdef SWITCH_PICO_LOG +#define LOG_PRINTF(...) printf(__VA_ARGS__) +#else +#define LOG_PRINTF(...) ((void)0) +#endif + // force a report to be sent every X ms #define SWITCH_PRO_KEEPALIVE_TIMER 5 @@ -180,7 +187,7 @@ static bool send_report(uint8_t reportID, const void* reportData, uint16_t repor last_report_counter = 0; } if (!result) { - printf("[HID] send_report failed id=%u len=%u\n", reportID, reportLength); + LOG_PRINTF("[HID] send_report failed id=%u len=%u\n", reportID, reportLength); } return result; } @@ -207,19 +214,19 @@ static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSub case IDENTIFY: send_identify(); canSend = true; - printf("[HID] CONFIG IDENTIFY\n"); + LOG_PRINTF("[HID] CONFIG IDENTIFY\n"); break; case HANDSHAKE: report_buffer[0] = REPORT_USB_INPUT_81; report_buffer[1] = HANDSHAKE; canSend = true; - printf("[HID] CONFIG HANDSHAKE\n"); + LOG_PRINTF("[HID] CONFIG HANDSHAKE\n"); break; case BAUD_RATE: report_buffer[0] = REPORT_USB_INPUT_81; report_buffer[1] = BAUD_RATE; canSend = true; - printf("[HID] CONFIG BAUD_RATE\n"); + LOG_PRINTF("[HID] CONFIG BAUD_RATE\n"); break; case DISABLE_USB_TIMEOUT: report_buffer[0] = REPORT_OUTPUT_30; @@ -230,19 +237,19 @@ static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSub is_ready = true; //} canSend = true; - printf("[HID] CONFIG DISABLE_USB_TIMEOUT -> ready\n"); + LOG_PRINTF("[HID] CONFIG DISABLE_USB_TIMEOUT -> ready\n"); break; case ENABLE_USB_TIMEOUT: report_buffer[0] = REPORT_OUTPUT_30; report_buffer[1] = switchReportSubID; canSend = true; - printf("[HID] CONFIG ENABLE_USB_TIMEOUT\n"); + LOG_PRINTF("[HID] CONFIG ENABLE_USB_TIMEOUT\n"); break; default: report_buffer[0] = REPORT_OUTPUT_30; report_buffer[1] = switchReportSubID; canSend = true; - printf("[HID] CONFIG unknown subid=0x%02x\n", switchReportSubID); + LOG_PRINTF("[HID] CONFIG unknown subid=0x%02x\n", switchReportSubID); break; } @@ -267,21 +274,21 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; - printf("[HID] FEATURE GET_CONTROLLER_STATE\n"); + LOG_PRINTF("[HID] FEATURE GET_CONTROLLER_STATE\n"); break; case BLUETOOTH_PAIR_REQUEST: report_buffer[13] = 0x81; report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; - printf("[HID] FEATURE BLUETOOTH_PAIR_REQUEST\n"); + LOG_PRINTF("[HID] FEATURE BLUETOOTH_PAIR_REQUEST\n"); break; case REQUEST_DEVICE_INFO: report_buffer[13] = 0x82; report_buffer[14] = 0x02; memcpy(&report_buffer[15], &device_info, sizeof(device_info)); canSend = true; - printf("[HID] FEATURE REQUEST_DEVICE_INFO\n"); + LOG_PRINTF("[HID] FEATURE REQUEST_DEVICE_INFO\n"); break; case SET_MODE: input_mode = reportData[11]; @@ -289,19 +296,19 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = 0x03; report_buffer[15] = input_mode; canSend = true; - printf("[HID] FEATURE SET_MODE 0x%02x\n", input_mode); + LOG_PRINTF("[HID] FEATURE SET_MODE 0x%02x\n", input_mode); break; case TRIGGER_BUTTONS: report_buffer[13] = 0x83; report_buffer[14] = 0x04; canSend = true; - printf("[HID] FEATURE TRIGGER_BUTTONS\n"); + LOG_PRINTF("[HID] FEATURE TRIGGER_BUTTONS\n"); break; case SET_SHIPMENT: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; - printf("[HID] FEATURE SET_SHIPMENT\n"); + LOG_PRINTF("[HID] FEATURE SET_SHIPMENT\n"); break; case SPI_READ: spiReadAddress = (reportData[14] << 24) | (reportData[13] << 16) | (reportData[12] << 8) | (reportData[11]); @@ -315,26 +322,26 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[19] = reportData[15]; read_spi_flash(&report_buffer[20], spiReadAddress, spiReadSize); canSend = true; - printf("[HID] FEATURE SPI_READ addr=0x%08lx size=%u\n", (unsigned long)spiReadAddress, spiReadSize); + LOG_PRINTF("[HID] FEATURE SPI_READ addr=0x%08lx size=%u\n", (unsigned long)spiReadAddress, spiReadSize); break; case SET_NFC_IR_CONFIG: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; - printf("[HID] FEATURE SET_NFC_IR_CONFIG\n"); + LOG_PRINTF("[HID] FEATURE SET_NFC_IR_CONFIG\n"); break; case SET_NFC_IR_STATE: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; - printf("[HID] FEATURE SET_NFC_IR_STATE\n"); + LOG_PRINTF("[HID] FEATURE SET_NFC_IR_STATE\n"); break; case SET_PLAYER_LIGHTS: player_id = reportData[11]; report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; - printf("[HID] FEATURE SET_PLAYER_LIGHTS player=%u\n", player_id); + LOG_PRINTF("[HID] FEATURE SET_PLAYER_LIGHTS player=%u\n", player_id); break; case GET_PLAYER_LIGHTS: player_id = reportData[11]; @@ -342,21 +349,21 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = player_id; canSend = true; - printf("[HID] FEATURE GET_PLAYER_LIGHTS player=%u\n", player_id); + LOG_PRINTF("[HID] FEATURE GET_PLAYER_LIGHTS player=%u\n", player_id); break; case COMMAND_UNKNOWN_33: report_buffer[13] = 0x80; report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; - printf("[HID] FEATURE COMMAND_UNKNOWN_33\n"); + LOG_PRINTF("[HID] FEATURE COMMAND_UNKNOWN_33\n"); break; case SET_HOME_LIGHT: report_buffer[13] = 0x80; report_buffer[14] = commandID; report_buffer[15] = 0x00; canSend = true; - printf("[HID] FEATURE SET_HOME_LIGHT\n"); + LOG_PRINTF("[HID] FEATURE SET_HOME_LIGHT\n"); break; case TOGGLE_IMU: is_imu_enabled = reportData[11]; @@ -364,13 +371,13 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = 0x00; canSend = true; - printf("[HID] FEATURE TOGGLE_IMU %u\n", is_imu_enabled); + LOG_PRINTF("[HID] FEATURE TOGGLE_IMU %u\n", is_imu_enabled); break; case IMU_SENSITIVITY: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; - printf("[HID] FEATURE IMU_SENSITIVITY\n"); + LOG_PRINTF("[HID] FEATURE IMU_SENSITIVITY\n"); break; case ENABLE_VIBRATION: is_vibration_enabled = reportData[11]; @@ -378,7 +385,7 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = 0x00; canSend = true; - printf("[HID] FEATURE ENABLE_VIBRATION %u\n", is_vibration_enabled); + LOG_PRINTF("[HID] FEATURE ENABLE_VIBRATION %u\n", is_vibration_enabled); break; case READ_IMU: report_buffer[13] = 0xC0; @@ -386,7 +393,7 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[15] = reportData[11]; report_buffer[16] = reportData[12]; canSend = true; - printf("[HID] FEATURE READ_IMU addr=%u size=%u\n", reportData[11], reportData[12]); + LOG_PRINTF("[HID] FEATURE READ_IMU addr=%u size=%u\n", reportData[11], reportData[12]); break; case GET_VOLTAGE: report_buffer[13] = 0xD0; @@ -394,14 +401,14 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[15] = 0x83; report_buffer[16] = 0x06; canSend = true; - printf("[HID] FEATURE GET_VOLTAGE\n"); + LOG_PRINTF("[HID] FEATURE GET_VOLTAGE\n"); break; default: report_buffer[13] = 0x80; report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; - printf("[HID] FEATURE unknown cmd=0x%02x\n", commandID); + LOG_PRINTF("[HID] FEATURE unknown cmd=0x%02x\n", commandID); break; } @@ -546,7 +553,12 @@ void switch_pro_task() { report_sent = true; } - // If the host never sends feature/config reports (seen on Switch), force readiness after a timeout. + // Start sending full reports only after the host has talked to us on OUT. + if (!is_ready && host_sent_out) { + is_ready = true; + LOG_PRINTF("[HID] ready because host sent OUT traffic\n"); + } + if (is_ready && !report_sent && host_sent_out) { if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) { switch_report.timestamp = last_report_counter; @@ -645,7 +657,7 @@ void switch_pro_mark_host_active() { // HID callbacks uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { (void)instance; - printf("[HID] get_report id=%u type=%u len=%u\n", report_id, report_type, reqlen); + LOG_PRINTF("[HID] get_report id=%u type=%u len=%u\n", report_id, report_type, reqlen); if (!buffer) return 0; // Serve the current input report for any GET_REPORT request. @@ -663,8 +675,8 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ uint8_t switchReportID = buffer[0]; uint8_t switchReportSubID = buffer[1]; - printf("[HID] set_report type=%d id=%u switchRID=0x%02x sub=0x%02x len=%u\n", - report_type, report_id, switchReportID, switchReportSubID, bufsize); + LOG_PRINTF("[HID] set_report type=%d id=%u switchRID=0x%02x sub=0x%02x len=%u\n", + report_type, report_id, switchReportID, switchReportSubID, bufsize); host_sent_out = true; if (switchReportID == REPORT_OUTPUT_00) { // No-op, just acknowledge to clear any stalls. @@ -685,8 +697,8 @@ void tud_hid_report_received_cb(uint8_t instance, uint8_t report_id, uint8_t con memset(report_buffer, 0x00, bufsize); uint8_t switchReportID = buffer[0]; uint8_t switchReportSubID = buffer[1]; - printf("[HID] report_received id=%u switchRID=0x%02x sub=0x%02x len=%u\n", - report_id, switchReportID, switchReportSubID, bufsize); + LOG_PRINTF("[HID] report_received id=%u switchRID=0x%02x sub=0x%02x len=%u\n", + report_id, switchReportID, switchReportSubID, bufsize); host_sent_out = true; if (switchReportID == REPORT_OUTPUT_00) { return; @@ -715,13 +727,13 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { bool tud_control_request_cb(uint8_t rhport, tusb_control_request_t const * request) { (void)rhport; - printf("[CTRL] bmReq=0x%02x bReq=0x%02x wValue=0x%04x wIndex=0x%04x wLen=%u\n", - request->bmRequestType, request->bRequest, request->wValue, request->wIndex, request->wLength); + LOG_PRINTF("[CTRL] bmReq=0x%02x bReq=0x%02x wValue=0x%04x wIndex=0x%04x wLen=%u\n", + request->bmRequestType, request->bRequest, request->wValue, request->wIndex, request->wLength); return false; // let TinyUSB handle it normally } void tud_mount_cb(void) { - printf("[USB] mount_cb\n"); + LOG_PRINTF("[USB] mount_cb\n"); last_host_activity_ms = to_ms_since_boot(get_absolute_time()); forced_ready = false; is_ready = false; @@ -730,7 +742,7 @@ void tud_mount_cb(void) { } void tud_umount_cb(void) { - printf("[USB] umount_cb\n"); + LOG_PRINTF("[USB] umount_cb\n"); forced_ready = false; is_ready = false; is_initialized = false; diff --git a/tusb_config.h b/tusb_config.h index 9f48992..aaf39e7 100644 --- a/tusb_config.h +++ b/tusb_config.h @@ -28,6 +28,7 @@ extern "C" { #define CFG_TUD_MSC 0 #define CFG_TUD_MIDI 0 #define CFG_TUD_VENDOR 0 +// Always enable TinyUSB debug at level 2; LOG_PRINTF controls user-facing logs. #ifdef CFG_TUSB_DEBUG #undef CFG_TUSB_DEBUG #endif From 6f6d28fdf4ffe5b46e37cb6e322e7dd073ead527 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 17:39:53 -0700 Subject: [PATCH 006/134] Remove debug logging if flag is present --- .gitignore | 2 ++ CMakeLists.txt | 4 +++ host_uart_logger.py | 2 +- switch-pico.cpp | 58 ++++++++++++++++-------------- switch_pro_driver.cpp | 84 ++++++++++++++++++++++++------------------- tusb_config.h | 1 + 6 files changed, 88 insertions(+), 63 deletions(-) diff --git a/.gitignore b/.gitignore index 4ed84ef..d9e1db8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.cache +Switch-Fightstick GP2040-CE build !.vscode/* diff --git a/CMakeLists.txt b/CMakeLists.txt index 68779eb..896fe25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ if (EXISTS ${picoVscode}) endif() # ==================================================================================== option(SWITCH_PICO_AUTOTEST "Auto-play grip-screen connection sequence on startup" OFF) +option(SWITCH_PICO_LOG "Enable UART debug logging" OFF) set(PICO_BOARD pico CACHE STRING "Board type") # Pull in Raspberry Pi Pico SDK (must be before project) @@ -61,6 +62,9 @@ target_link_libraries(switch-pico if (SWITCH_PICO_AUTOTEST) target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_AUTOTEST=1) endif() +if (SWITCH_PICO_LOG) + target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_LOG=1) +endif() # Add the standard include files to the build target_include_directories(switch-pico PRIVATE diff --git a/host_uart_logger.py b/host_uart_logger.py index 634db3a..658f3ca 100644 --- a/host_uart_logger.py +++ b/host_uart_logger.py @@ -12,7 +12,7 @@ except ImportError: def main(): parser = argparse.ArgumentParser(description="Read debug logs from Pico UART") parser.add_argument("-p", "--port", required=True, help="Serial port (e.g. /dev/ttyUSB0 or COM3)") - parser.add_argument("-b", "--baud", type=int, default=115200, help="Baud rate (default: 115200)") + parser.add_argument("-b", "--baud", type=int, default=1000000, help="Baud rate (default: 1000000)") args = parser.parse_args() with serial.Serial(args.port, args.baud, timeout=1) as ser: diff --git a/switch-pico.cpp b/switch-pico.cpp index 70f1b21..1d403de 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -5,9 +5,15 @@ #include "tusb.h" #include "switch_pro_driver.h" +#ifdef SWITCH_PICO_LOG +#define LOG_PRINTF(...) printf(__VA_ARGS__) +#else +#define LOG_PRINTF(...) ((void)0) +#endif + // UART1 is reserved for external input frames from the host PC. #define UART_ID uart1 -#define BAUD_RATE 115200 +#define BAUD_RATE 1000000 #define UART_TX_PIN 4 #define UART_RX_PIN 5 @@ -69,26 +75,26 @@ static void poll_uart_frames() { SwitchInputState parsed{}; if (switch_pro_apply_uart_packet(buffer, sizeof(buffer), &parsed)) { g_user_state = parsed; - printf("[UART] packet buttons=0x%04x hat=%u lx=%u ly=%u rx=%u ry=%u\n", - (parsed.button_a ? SWITCH_PRO_MASK_A : 0) | - (parsed.button_b ? SWITCH_PRO_MASK_B : 0) | - (parsed.button_x ? SWITCH_PRO_MASK_X : 0) | - (parsed.button_y ? SWITCH_PRO_MASK_Y : 0) | - (parsed.button_l ? SWITCH_PRO_MASK_L : 0) | - (parsed.button_r ? SWITCH_PRO_MASK_R : 0) | - (parsed.button_zl ? SWITCH_PRO_MASK_ZL : 0) | - (parsed.button_zr ? SWITCH_PRO_MASK_ZR : 0) | - (parsed.button_plus? SWITCH_PRO_MASK_PLUS: 0) | - (parsed.button_minus?SWITCH_PRO_MASK_MINUS:0) | - (parsed.button_home?SWITCH_PRO_MASK_HOME:0) | - (parsed.button_capture?SWITCH_PRO_MASK_CAPTURE:0) | - (parsed.button_l3 ? SWITCH_PRO_MASK_L3 : 0) | - (parsed.button_r3 ? SWITCH_PRO_MASK_R3 : 0), - parsed.dpad_up ? SWITCH_PRO_HAT_UP : - parsed.dpad_down ? SWITCH_PRO_HAT_DOWN : - parsed.dpad_left ? SWITCH_PRO_HAT_LEFT : - parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING, - parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8); + LOG_PRINTF("[UART] packet buttons=0x%04x hat=%u lx=%u ly=%u rx=%u ry=%u\n", + (parsed.button_a ? SWITCH_PRO_MASK_A : 0) | + (parsed.button_b ? SWITCH_PRO_MASK_B : 0) | + (parsed.button_x ? SWITCH_PRO_MASK_X : 0) | + (parsed.button_y ? SWITCH_PRO_MASK_Y : 0) | + (parsed.button_l ? SWITCH_PRO_MASK_L : 0) | + (parsed.button_r ? SWITCH_PRO_MASK_R : 0) | + (parsed.button_zl ? SWITCH_PRO_MASK_ZL : 0) | + (parsed.button_zr ? SWITCH_PRO_MASK_ZR : 0) | + (parsed.button_plus? SWITCH_PRO_MASK_PLUS: 0) | + (parsed.button_minus?SWITCH_PRO_MASK_MINUS:0) | + (parsed.button_home?SWITCH_PRO_MASK_HOME:0) | + (parsed.button_capture?SWITCH_PRO_MASK_CAPTURE:0) | + (parsed.button_l3 ? SWITCH_PRO_MASK_L3 : 0) | + (parsed.button_r3 ? SWITCH_PRO_MASK_R3 : 0), + parsed.dpad_up ? SWITCH_PRO_HAT_UP : + parsed.dpad_down ? SWITCH_PRO_HAT_DOWN : + parsed.dpad_left ? SWITCH_PRO_HAT_LEFT : + parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING, + parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8); } #ifdef SWITCH_PICO_AUTOTEST g_uart_activity = true; @@ -150,16 +156,16 @@ static void log_usb_state() { if (mounted != g_last_mounted) { g_last_mounted = mounted; - printf("[USB] %s\n", mounted ? "mounted" : "unmounted"); + LOG_PRINTF("[USB] %s\n", mounted ? "mounted" : "unmounted"); } if (ready != g_last_ready) { g_last_ready = ready; - printf("[SWITCH] driver %s\n", ready ? "ready (handshake OK)" : "not ready"); + LOG_PRINTF("[SWITCH] driver %s\n", ready ? "ready (handshake OK)" : "not ready"); } #ifdef SWITCH_PICO_AUTOTEST if (ready && !g_ready_logged) { g_ready_logged = true; - printf("[AUTO] ready -> autopilot active=%s\n", g_autopilot_active ? "true" : "false"); + LOG_PRINTF("[AUTO] ready -> autopilot active=%s\n", g_autopilot_active ? "true" : "false"); } #endif } @@ -175,8 +181,8 @@ int main() { g_user_state = neutral_input(); switch_pro_set_input(g_user_state); - printf("[BOOT] switch-pico starting (UART0 log @ 115200)\n"); - printf("[INFO] AUTOTEST=%s UART1 pins TX=%d RX=%d baud=%d\n", + LOG_PRINTF("[BOOT] switch-pico starting (UART0 log @ 115200)\n"); + LOG_PRINTF("[INFO] AUTOTEST=%s UART1 pins TX=%d RX=%d baud=%d\n", #ifdef SWITCH_PICO_AUTOTEST "ON" #else diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index 97158c1..63765db 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -3,10 +3,17 @@ #include #include #include +#include #include "pico/rand.h" #include "pico/time.h" #include "tusb.h" +#ifdef SWITCH_PICO_LOG +#define LOG_PRINTF(...) printf(__VA_ARGS__) +#else +#define LOG_PRINTF(...) ((void)0) +#endif + // force a report to be sent every X ms #define SWITCH_PRO_KEEPALIVE_TIMER 5 @@ -180,7 +187,7 @@ static bool send_report(uint8_t reportID, const void* reportData, uint16_t repor last_report_counter = 0; } if (!result) { - printf("[HID] send_report failed id=%u len=%u\n", reportID, reportLength); + LOG_PRINTF("[HID] send_report failed id=%u len=%u\n", reportID, reportLength); } return result; } @@ -207,19 +214,19 @@ static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSub case IDENTIFY: send_identify(); canSend = true; - printf("[HID] CONFIG IDENTIFY\n"); + LOG_PRINTF("[HID] CONFIG IDENTIFY\n"); break; case HANDSHAKE: report_buffer[0] = REPORT_USB_INPUT_81; report_buffer[1] = HANDSHAKE; canSend = true; - printf("[HID] CONFIG HANDSHAKE\n"); + LOG_PRINTF("[HID] CONFIG HANDSHAKE\n"); break; case BAUD_RATE: report_buffer[0] = REPORT_USB_INPUT_81; report_buffer[1] = BAUD_RATE; canSend = true; - printf("[HID] CONFIG BAUD_RATE\n"); + LOG_PRINTF("[HID] CONFIG BAUD_RATE\n"); break; case DISABLE_USB_TIMEOUT: report_buffer[0] = REPORT_OUTPUT_30; @@ -230,19 +237,19 @@ static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSub is_ready = true; //} canSend = true; - printf("[HID] CONFIG DISABLE_USB_TIMEOUT -> ready\n"); + LOG_PRINTF("[HID] CONFIG DISABLE_USB_TIMEOUT -> ready\n"); break; case ENABLE_USB_TIMEOUT: report_buffer[0] = REPORT_OUTPUT_30; report_buffer[1] = switchReportSubID; canSend = true; - printf("[HID] CONFIG ENABLE_USB_TIMEOUT\n"); + LOG_PRINTF("[HID] CONFIG ENABLE_USB_TIMEOUT\n"); break; default: report_buffer[0] = REPORT_OUTPUT_30; report_buffer[1] = switchReportSubID; canSend = true; - printf("[HID] CONFIG unknown subid=0x%02x\n", switchReportSubID); + LOG_PRINTF("[HID] CONFIG unknown subid=0x%02x\n", switchReportSubID); break; } @@ -267,21 +274,21 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; - printf("[HID] FEATURE GET_CONTROLLER_STATE\n"); + LOG_PRINTF("[HID] FEATURE GET_CONTROLLER_STATE\n"); break; case BLUETOOTH_PAIR_REQUEST: report_buffer[13] = 0x81; report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; - printf("[HID] FEATURE BLUETOOTH_PAIR_REQUEST\n"); + LOG_PRINTF("[HID] FEATURE BLUETOOTH_PAIR_REQUEST\n"); break; case REQUEST_DEVICE_INFO: report_buffer[13] = 0x82; report_buffer[14] = 0x02; memcpy(&report_buffer[15], &device_info, sizeof(device_info)); canSend = true; - printf("[HID] FEATURE REQUEST_DEVICE_INFO\n"); + LOG_PRINTF("[HID] FEATURE REQUEST_DEVICE_INFO\n"); break; case SET_MODE: input_mode = reportData[11]; @@ -289,19 +296,19 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = 0x03; report_buffer[15] = input_mode; canSend = true; - printf("[HID] FEATURE SET_MODE 0x%02x\n", input_mode); + LOG_PRINTF("[HID] FEATURE SET_MODE 0x%02x\n", input_mode); break; case TRIGGER_BUTTONS: report_buffer[13] = 0x83; report_buffer[14] = 0x04; canSend = true; - printf("[HID] FEATURE TRIGGER_BUTTONS\n"); + LOG_PRINTF("[HID] FEATURE TRIGGER_BUTTONS\n"); break; case SET_SHIPMENT: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; - printf("[HID] FEATURE SET_SHIPMENT\n"); + LOG_PRINTF("[HID] FEATURE SET_SHIPMENT\n"); break; case SPI_READ: spiReadAddress = (reportData[14] << 24) | (reportData[13] << 16) | (reportData[12] << 8) | (reportData[11]); @@ -315,26 +322,26 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[19] = reportData[15]; read_spi_flash(&report_buffer[20], spiReadAddress, spiReadSize); canSend = true; - printf("[HID] FEATURE SPI_READ addr=0x%08lx size=%u\n", (unsigned long)spiReadAddress, spiReadSize); + LOG_PRINTF("[HID] FEATURE SPI_READ addr=0x%08lx size=%u\n", (unsigned long)spiReadAddress, spiReadSize); break; case SET_NFC_IR_CONFIG: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; - printf("[HID] FEATURE SET_NFC_IR_CONFIG\n"); + LOG_PRINTF("[HID] FEATURE SET_NFC_IR_CONFIG\n"); break; case SET_NFC_IR_STATE: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; - printf("[HID] FEATURE SET_NFC_IR_STATE\n"); + LOG_PRINTF("[HID] FEATURE SET_NFC_IR_STATE\n"); break; case SET_PLAYER_LIGHTS: player_id = reportData[11]; report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; - printf("[HID] FEATURE SET_PLAYER_LIGHTS player=%u\n", player_id); + LOG_PRINTF("[HID] FEATURE SET_PLAYER_LIGHTS player=%u\n", player_id); break; case GET_PLAYER_LIGHTS: player_id = reportData[11]; @@ -342,21 +349,21 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = player_id; canSend = true; - printf("[HID] FEATURE GET_PLAYER_LIGHTS player=%u\n", player_id); + LOG_PRINTF("[HID] FEATURE GET_PLAYER_LIGHTS player=%u\n", player_id); break; case COMMAND_UNKNOWN_33: report_buffer[13] = 0x80; report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; - printf("[HID] FEATURE COMMAND_UNKNOWN_33\n"); + LOG_PRINTF("[HID] FEATURE COMMAND_UNKNOWN_33\n"); break; case SET_HOME_LIGHT: report_buffer[13] = 0x80; report_buffer[14] = commandID; report_buffer[15] = 0x00; canSend = true; - printf("[HID] FEATURE SET_HOME_LIGHT\n"); + LOG_PRINTF("[HID] FEATURE SET_HOME_LIGHT\n"); break; case TOGGLE_IMU: is_imu_enabled = reportData[11]; @@ -364,13 +371,13 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = 0x00; canSend = true; - printf("[HID] FEATURE TOGGLE_IMU %u\n", is_imu_enabled); + LOG_PRINTF("[HID] FEATURE TOGGLE_IMU %u\n", is_imu_enabled); break; case IMU_SENSITIVITY: report_buffer[13] = 0x80; report_buffer[14] = commandID; canSend = true; - printf("[HID] FEATURE IMU_SENSITIVITY\n"); + LOG_PRINTF("[HID] FEATURE IMU_SENSITIVITY\n"); break; case ENABLE_VIBRATION: is_vibration_enabled = reportData[11]; @@ -378,7 +385,7 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[14] = commandID; report_buffer[15] = 0x00; canSend = true; - printf("[HID] FEATURE ENABLE_VIBRATION %u\n", is_vibration_enabled); + LOG_PRINTF("[HID] FEATURE ENABLE_VIBRATION %u\n", is_vibration_enabled); break; case READ_IMU: report_buffer[13] = 0xC0; @@ -386,7 +393,7 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[15] = reportData[11]; report_buffer[16] = reportData[12]; canSend = true; - printf("[HID] FEATURE READ_IMU addr=%u size=%u\n", reportData[11], reportData[12]); + LOG_PRINTF("[HID] FEATURE READ_IMU addr=%u size=%u\n", reportData[11], reportData[12]); break; case GET_VOLTAGE: report_buffer[13] = 0xD0; @@ -394,14 +401,14 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu report_buffer[15] = 0x83; report_buffer[16] = 0x06; canSend = true; - printf("[HID] FEATURE GET_VOLTAGE\n"); + LOG_PRINTF("[HID] FEATURE GET_VOLTAGE\n"); break; default: report_buffer[13] = 0x80; report_buffer[14] = commandID; report_buffer[15] = 0x03; canSend = true; - printf("[HID] FEATURE unknown cmd=0x%02x\n", commandID); + LOG_PRINTF("[HID] FEATURE unknown cmd=0x%02x\n", commandID); break; } @@ -546,7 +553,12 @@ void switch_pro_task() { report_sent = true; } - // If the host never sends feature/config reports (seen on Switch), force readiness after a timeout. + // Start sending full reports only after the host has talked to us on OUT. + if (!is_ready && host_sent_out) { + is_ready = true; + LOG_PRINTF("[HID] ready because host sent OUT traffic\n"); + } + if (is_ready && !report_sent && host_sent_out) { if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) { switch_report.timestamp = last_report_counter; @@ -645,7 +657,7 @@ void switch_pro_mark_host_active() { // HID callbacks uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { (void)instance; - printf("[HID] get_report id=%u type=%u len=%u\n", report_id, report_type, reqlen); + LOG_PRINTF("[HID] get_report id=%u type=%u len=%u\n", report_id, report_type, reqlen); if (!buffer) return 0; // Serve the current input report for any GET_REPORT request. @@ -663,8 +675,8 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ uint8_t switchReportID = buffer[0]; uint8_t switchReportSubID = buffer[1]; - printf("[HID] set_report type=%d id=%u switchRID=0x%02x sub=0x%02x len=%u\n", - report_type, report_id, switchReportID, switchReportSubID, bufsize); + LOG_PRINTF("[HID] set_report type=%d id=%u switchRID=0x%02x sub=0x%02x len=%u\n", + report_type, report_id, switchReportID, switchReportSubID, bufsize); host_sent_out = true; if (switchReportID == REPORT_OUTPUT_00) { // No-op, just acknowledge to clear any stalls. @@ -685,8 +697,8 @@ void tud_hid_report_received_cb(uint8_t instance, uint8_t report_id, uint8_t con memset(report_buffer, 0x00, bufsize); uint8_t switchReportID = buffer[0]; uint8_t switchReportSubID = buffer[1]; - printf("[HID] report_received id=%u switchRID=0x%02x sub=0x%02x len=%u\n", - report_id, switchReportID, switchReportSubID, bufsize); + LOG_PRINTF("[HID] report_received id=%u switchRID=0x%02x sub=0x%02x len=%u\n", + report_id, switchReportID, switchReportSubID, bufsize); host_sent_out = true; if (switchReportID == REPORT_OUTPUT_00) { return; @@ -715,13 +727,13 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { bool tud_control_request_cb(uint8_t rhport, tusb_control_request_t const * request) { (void)rhport; - printf("[CTRL] bmReq=0x%02x bReq=0x%02x wValue=0x%04x wIndex=0x%04x wLen=%u\n", - request->bmRequestType, request->bRequest, request->wValue, request->wIndex, request->wLength); + LOG_PRINTF("[CTRL] bmReq=0x%02x bReq=0x%02x wValue=0x%04x wIndex=0x%04x wLen=%u\n", + request->bmRequestType, request->bRequest, request->wValue, request->wIndex, request->wLength); return false; // let TinyUSB handle it normally } void tud_mount_cb(void) { - printf("[USB] mount_cb\n"); + LOG_PRINTF("[USB] mount_cb\n"); last_host_activity_ms = to_ms_since_boot(get_absolute_time()); forced_ready = false; is_ready = false; @@ -730,7 +742,7 @@ void tud_mount_cb(void) { } void tud_umount_cb(void) { - printf("[USB] umount_cb\n"); + LOG_PRINTF("[USB] umount_cb\n"); forced_ready = false; is_ready = false; is_initialized = false; diff --git a/tusb_config.h b/tusb_config.h index 9f48992..aaf39e7 100644 --- a/tusb_config.h +++ b/tusb_config.h @@ -28,6 +28,7 @@ extern "C" { #define CFG_TUD_MSC 0 #define CFG_TUD_MIDI 0 #define CFG_TUD_VENDOR 0 +// Always enable TinyUSB debug at level 2; LOG_PRINTF controls user-facing logs. #ifdef CFG_TUSB_DEBUG #undef CFG_TUSB_DEBUG #endif From a6ad93d184f5b40e54e22d464b4ba9c46ab54257 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 18:02:54 -0700 Subject: [PATCH 007/134] WIP No debug --- switch_pro_driver.cpp | 24 +++++------------------- switch_pro_driver.h | 3 --- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index 63765db..12b2f74 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -36,7 +36,6 @@ static bool is_report_queued = false; static bool report_sent = false; static uint8_t queued_report_id = 0; static bool forced_ready = false; -static bool host_sent_out = false; static uint8_t handshake_counter = 0; static SwitchDeviceInfo device_info{}; @@ -208,7 +207,6 @@ static void read_spi_flash(uint8_t* dest, uint32_t address, uint8_t size) { static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) { bool canSend = false; last_host_activity_ms = to_ms_since_boot(get_absolute_time()); - host_sent_out = true; switch (switchReportSubID) { case IDENTIFY: @@ -262,7 +260,6 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu uint8_t spiReadSize = 0; bool canSend = false; last_host_activity_ms = to_ms_since_boot(get_absolute_time()); - host_sent_out = true; report_buffer[0] = REPORT_OUTPUT_21; report_buffer[1] = last_report_counter; @@ -464,7 +461,10 @@ void switch_pro_init() { is_report_queued = false; report_sent = false; forced_ready = false; - host_sent_out = false; + forced_ready = true; + is_ready = true; + is_initialized = true; + last_report_timer = 0; device_info = { .majorVersion = 0x04, @@ -553,13 +553,7 @@ void switch_pro_task() { report_sent = true; } - // Start sending full reports only after the host has talked to us on OUT. - if (!is_ready && host_sent_out) { - is_ready = true; - LOG_PRINTF("[HID] ready because host sent OUT traffic\n"); - } - - if (is_ready && !report_sent && host_sent_out) { + if (is_ready && !report_sent) { if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) { switch_report.timestamp = last_report_counter; void * inputReport = &switch_report; @@ -650,10 +644,6 @@ bool switch_pro_is_ready() { return is_ready; } -void switch_pro_mark_host_active() { - host_sent_out = true; -} - // HID callbacks uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { (void)instance; @@ -677,7 +667,6 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ uint8_t switchReportSubID = buffer[1]; LOG_PRINTF("[HID] set_report type=%d id=%u switchRID=0x%02x sub=0x%02x len=%u\n", report_type, report_id, switchReportID, switchReportSubID, bufsize); - host_sent_out = true; if (switchReportID == REPORT_OUTPUT_00) { // No-op, just acknowledge to clear any stalls. return; @@ -699,7 +688,6 @@ void tud_hid_report_received_cb(uint8_t instance, uint8_t report_id, uint8_t con uint8_t switchReportSubID = buffer[1]; LOG_PRINTF("[HID] report_received id=%u switchRID=0x%02x sub=0x%02x len=%u\n", report_id, switchReportID, switchReportSubID, bufsize); - host_sent_out = true; if (switchReportID == REPORT_OUTPUT_00) { return; } else if (switchReportID == REPORT_FEATURE) { @@ -738,7 +726,6 @@ void tud_mount_cb(void) { forced_ready = false; is_ready = false; is_initialized = false; - host_sent_out = false; } void tud_umount_cb(void) { @@ -746,7 +733,6 @@ void tud_umount_cb(void) { forced_ready = false; is_ready = false; is_initialized = false; - host_sent_out = false; } static uint16_t desc_str[32]; diff --git a/switch_pro_driver.h b/switch_pro_driver.h index 7fb2dc4..79d7070 100644 --- a/switch_pro_driver.h +++ b/switch_pro_driver.h @@ -52,6 +52,3 @@ bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchI // Driver state helpers bool switch_pro_is_ready(); - -// Mark that the host has sent any OUT traffic (allows starting IN reports). -void switch_pro_mark_host_active(); From 1e0ff5aaaad70bd71d954e6c6ec9eaffe8d138d8 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 18:02:54 -0700 Subject: [PATCH 008/134] WIP No debug --- switch_pro_driver.cpp | 24 +++++------------------- switch_pro_driver.h | 3 --- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index 63765db..12b2f74 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -36,7 +36,6 @@ static bool is_report_queued = false; static bool report_sent = false; static uint8_t queued_report_id = 0; static bool forced_ready = false; -static bool host_sent_out = false; static uint8_t handshake_counter = 0; static SwitchDeviceInfo device_info{}; @@ -208,7 +207,6 @@ static void read_spi_flash(uint8_t* dest, uint32_t address, uint8_t size) { static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) { bool canSend = false; last_host_activity_ms = to_ms_since_boot(get_absolute_time()); - host_sent_out = true; switch (switchReportSubID) { case IDENTIFY: @@ -262,7 +260,6 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu uint8_t spiReadSize = 0; bool canSend = false; last_host_activity_ms = to_ms_since_boot(get_absolute_time()); - host_sent_out = true; report_buffer[0] = REPORT_OUTPUT_21; report_buffer[1] = last_report_counter; @@ -464,7 +461,10 @@ void switch_pro_init() { is_report_queued = false; report_sent = false; forced_ready = false; - host_sent_out = false; + forced_ready = true; + is_ready = true; + is_initialized = true; + last_report_timer = 0; device_info = { .majorVersion = 0x04, @@ -553,13 +553,7 @@ void switch_pro_task() { report_sent = true; } - // Start sending full reports only after the host has talked to us on OUT. - if (!is_ready && host_sent_out) { - is_ready = true; - LOG_PRINTF("[HID] ready because host sent OUT traffic\n"); - } - - if (is_ready && !report_sent && host_sent_out) { + if (is_ready && !report_sent) { if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) { switch_report.timestamp = last_report_counter; void * inputReport = &switch_report; @@ -650,10 +644,6 @@ bool switch_pro_is_ready() { return is_ready; } -void switch_pro_mark_host_active() { - host_sent_out = true; -} - // HID callbacks uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { (void)instance; @@ -677,7 +667,6 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ uint8_t switchReportSubID = buffer[1]; LOG_PRINTF("[HID] set_report type=%d id=%u switchRID=0x%02x sub=0x%02x len=%u\n", report_type, report_id, switchReportID, switchReportSubID, bufsize); - host_sent_out = true; if (switchReportID == REPORT_OUTPUT_00) { // No-op, just acknowledge to clear any stalls. return; @@ -699,7 +688,6 @@ void tud_hid_report_received_cb(uint8_t instance, uint8_t report_id, uint8_t con uint8_t switchReportSubID = buffer[1]; LOG_PRINTF("[HID] report_received id=%u switchRID=0x%02x sub=0x%02x len=%u\n", report_id, switchReportID, switchReportSubID, bufsize); - host_sent_out = true; if (switchReportID == REPORT_OUTPUT_00) { return; } else if (switchReportID == REPORT_FEATURE) { @@ -738,7 +726,6 @@ void tud_mount_cb(void) { forced_ready = false; is_ready = false; is_initialized = false; - host_sent_out = false; } void tud_umount_cb(void) { @@ -746,7 +733,6 @@ void tud_umount_cb(void) { forced_ready = false; is_ready = false; is_initialized = false; - host_sent_out = false; } static uint16_t desc_str[32]; diff --git a/switch_pro_driver.h b/switch_pro_driver.h index 7fb2dc4..79d7070 100644 --- a/switch_pro_driver.h +++ b/switch_pro_driver.h @@ -52,6 +52,3 @@ bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchI // Driver state helpers bool switch_pro_is_ready(); - -// Mark that the host has sent any OUT traffic (allows starting IN reports). -void switch_pro_mark_host_active(); From b07801e11f49647bc13a92ca3dad314a7949a7c8 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 18:03:06 -0700 Subject: [PATCH 009/134] WIP No debug 2 --- switch_pro_driver.cpp | 4 ++-- tusb_config.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index 12b2f74..7ae9475 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -546,9 +546,9 @@ void switch_pro_task() { if (is_report_queued) { if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) { if (tud_hid_ready() && send_report(queued_report_id, report_buffer, 64) == true ) { + is_report_queued = false; + last_report_timer = now; } - is_report_queued = false; - last_report_timer = now; } report_sent = true; } diff --git a/tusb_config.h b/tusb_config.h index aaf39e7..7f291ec 100644 --- a/tusb_config.h +++ b/tusb_config.h @@ -32,7 +32,7 @@ extern "C" { #ifdef CFG_TUSB_DEBUG #undef CFG_TUSB_DEBUG #endif -#define CFG_TUSB_DEBUG 2 +#define CFG_TUSB_DEBUG 0 #define CFG_TUD_HID_EP_BUFSIZE 64 From 96c62bd3874e65105c1ce6990406e0b2e321a6be Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 18:03:06 -0700 Subject: [PATCH 010/134] WIP No debug 2 --- switch_pro_driver.cpp | 4 ++-- tusb_config.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index 12b2f74..7ae9475 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -546,9 +546,9 @@ void switch_pro_task() { if (is_report_queued) { if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) { if (tud_hid_ready() && send_report(queued_report_id, report_buffer, 64) == true ) { + is_report_queued = false; + last_report_timer = now; } - is_report_queued = false; - last_report_timer = now; } report_sent = true; } diff --git a/tusb_config.h b/tusb_config.h index aaf39e7..7f291ec 100644 --- a/tusb_config.h +++ b/tusb_config.h @@ -32,7 +32,7 @@ extern "C" { #ifdef CFG_TUSB_DEBUG #undef CFG_TUSB_DEBUG #endif -#define CFG_TUSB_DEBUG 2 +#define CFG_TUSB_DEBUG 0 #define CFG_TUD_HID_EP_BUFSIZE 64 From db836463936a4e878692c00a0fe0dbd994264e39 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 18:53:27 -0700 Subject: [PATCH 011/134] 5ms timing --- switch-pico.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/switch-pico.cpp b/switch-pico.cpp index 1d403de..9bcdee7 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -136,13 +136,18 @@ static SwitchInputState autopilot_state(const SwitchInputState& fallback) { state.ry = SWITCH_PRO_JOYSTICK_MID; // Fire L+R twice then A twice, loop every ~300ms to keep trying. + // Hold each press for a few ms so it survives the 5ms USB report throttle. uint32_t step = g_autopilot_counter % 300; - if (step == 25 || step == 50) { - state.button_l = true; - state.button_r = true; - } else if (step == 75 || step == 100) { - state.button_a = true; - } + constexpr uint32_t press_width = 50; // ~12ms + auto in_window = [](uint32_t v, uint32_t start, uint32_t width) { + return v >= start && v < start + width; + }; + bool lr_down = in_window(step, 25, press_width) || in_window(step, 50, press_width); + bool a_down = in_window(step, 75, press_width) || in_window(step, 100, press_width); + + state.button_r = lr_down; + state.button_l = lr_down; + state.button_a = a_down; g_autopilot_counter++; From 79903c6f1bf17624eb2eb3cdb0407be22b862740 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 18:53:27 -0700 Subject: [PATCH 012/134] 5ms timing --- switch-pico.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/switch-pico.cpp b/switch-pico.cpp index 1d403de..9bcdee7 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -136,13 +136,18 @@ static SwitchInputState autopilot_state(const SwitchInputState& fallback) { state.ry = SWITCH_PRO_JOYSTICK_MID; // Fire L+R twice then A twice, loop every ~300ms to keep trying. + // Hold each press for a few ms so it survives the 5ms USB report throttle. uint32_t step = g_autopilot_counter % 300; - if (step == 25 || step == 50) { - state.button_l = true; - state.button_r = true; - } else if (step == 75 || step == 100) { - state.button_a = true; - } + constexpr uint32_t press_width = 50; // ~12ms + auto in_window = [](uint32_t v, uint32_t start, uint32_t width) { + return v >= start && v < start + width; + }; + bool lr_down = in_window(step, 25, press_width) || in_window(step, 50, press_width); + bool a_down = in_window(step, 75, press_width) || in_window(step, 100, press_width); + + state.button_r = lr_down; + state.button_l = lr_down; + state.button_a = a_down; g_autopilot_counter++; From de4c978a0708a4065d2948406b1c199206fb2ec9 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 22:40:36 -0700 Subject: [PATCH 013/134] WORKING --- .gitignore | 3 + host_uart_logger.py | 2 +- mac_controller_uart_bridge.py | 516 ++++++++++++++++++++++++++++++++++ switch-pico.cpp | 24 +- switch_pro_descriptors.h | 29 +- switch_pro_driver.cpp | 21 ++ switch_pro_driver.h | 4 + 7 files changed, 582 insertions(+), 17 deletions(-) create mode 100644 mac_controller_uart_bridge.py diff --git a/.gitignore b/.gitignore index d9e1db8..0c5aeec 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ Switch-Fightstick GP2040-CE build +debug +.pycache +__pycache__ !.vscode/* diff --git a/host_uart_logger.py b/host_uart_logger.py index 658f3ca..634db3a 100644 --- a/host_uart_logger.py +++ b/host_uart_logger.py @@ -12,7 +12,7 @@ except ImportError: def main(): parser = argparse.ArgumentParser(description="Read debug logs from Pico UART") parser.add_argument("-p", "--port", required=True, help="Serial port (e.g. /dev/ttyUSB0 or COM3)") - parser.add_argument("-b", "--baud", type=int, default=1000000, help="Baud rate (default: 1000000)") + parser.add_argument("-b", "--baud", type=int, default=115200, help="Baud rate (default: 115200)") args = parser.parse_args() with serial.Serial(args.port, args.baud, timeout=1) as ser: diff --git a/mac_controller_uart_bridge.py b/mac_controller_uart_bridge.py new file mode 100644 index 0000000..45bcc71 --- /dev/null +++ b/mac_controller_uart_bridge.py @@ -0,0 +1,516 @@ +#!/usr/bin/env python3 +""" +Bridge multiple SDL2 controllers to switch-pico over UART and mirror rumble back. + +The framing matches ``switch-pico.cpp``: + - Host -> Pico : 0xAA, buttons (LE16), hat, lx, ly, rx, ry + - Pico -> Host : 0xBB, 0x01, 8 rumble bytes, checksum (sum of first 10 bytes) + +Features inspired by ``host/controller_bridge.py``: + - Multiple controllers paired to multiple UART ports + - Rich-powered interactive pairing UI + - Adjustable send frequency, deadzone, and trigger thresholds + - Rumble feedback delivered to SDL2 controllers +""" + +from __future__ import annotations + +import argparse +import struct +import threading +import time +from dataclasses import dataclass, field +from typing import Dict, List, Optional, Tuple + +import serial +from serial.tools import list_ports + +import sdl2 +import sdl2.ext +from rich.console import Console +from rich.prompt import Prompt +from rich.table import Table + +UART_HEADER = 0xAA +RUMBLE_HEADER = 0xBB +RUMBLE_TYPE_RUMBLE = 0x01 +UART_BAUD = 900000 + + +class SwitchButton: + # Mirrors the masks defined in switch_pro_descriptors.h + Y = 1 << 0 + B = 1 << 1 + A = 1 << 2 + X = 1 << 3 + L = 1 << 4 + R = 1 << 5 + ZL = 1 << 6 + ZR = 1 << 7 + MINUS = 1 << 8 + PLUS = 1 << 9 + LCLICK = 1 << 10 + RCLICK = 1 << 11 + HOME = 1 << 12 + CAPTURE = 1 << 13 + + +class SwitchHat: + TOP = 0x00 + TOP_RIGHT = 0x01 + RIGHT = 0x02 + BOTTOM_RIGHT = 0x03 + BOTTOM = 0x04 + BOTTOM_LEFT = 0x05 + LEFT = 0x06 + TOP_LEFT = 0x07 + CENTER = 0x08 + + +def parse_mapping(value: str) -> Tuple[int, str]: + if ":" not in value: + raise argparse.ArgumentTypeError("Mapping must look like 'index:serial_port'") + idx_str, port = value.split(":", 1) + try: + idx = int(idx_str, 10) + except ValueError as exc: + raise argparse.ArgumentTypeError(f"Invalid controller index '{idx_str}'") from exc + if not port: + raise argparse.ArgumentTypeError("Serial port cannot be empty") + return idx, port.strip() + + +def axis_to_stick(value: int, deadzone: int) -> int: + if abs(value) < deadzone: + value = 0 + scaled = int((value + 32768) * 255 / 65535) + return max(0, min(255, scaled)) + + +def trigger_to_button(value: int, threshold: int) -> bool: + return value >= threshold + + +BUTTON_MAP = { + # Direct mapping to the Switch PRO mask definitions in switch_pro_descriptors.h + sdl2.SDL_CONTROLLER_BUTTON_A: SwitchButton.A, + sdl2.SDL_CONTROLLER_BUTTON_B: SwitchButton.B, + sdl2.SDL_CONTROLLER_BUTTON_X: SwitchButton.X, + sdl2.SDL_CONTROLLER_BUTTON_Y: SwitchButton.Y, + sdl2.SDL_CONTROLLER_BUTTON_LEFTSHOULDER: SwitchButton.L, + sdl2.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: SwitchButton.R, + sdl2.SDL_CONTROLLER_BUTTON_BACK: SwitchButton.MINUS, + sdl2.SDL_CONTROLLER_BUTTON_START: SwitchButton.PLUS, + sdl2.SDL_CONTROLLER_BUTTON_GUIDE: SwitchButton.HOME, + sdl2.SDL_CONTROLLER_BUTTON_MISC1: SwitchButton.CAPTURE, + sdl2.SDL_CONTROLLER_BUTTON_LEFTSTICK: SwitchButton.LCLICK, + sdl2.SDL_CONTROLLER_BUTTON_RIGHTSTICK: SwitchButton.RCLICK, +} + +DPAD_BUTTONS = { + sdl2.SDL_CONTROLLER_BUTTON_DPAD_UP: "up", + sdl2.SDL_CONTROLLER_BUTTON_DPAD_DOWN: "down", + sdl2.SDL_CONTROLLER_BUTTON_DPAD_LEFT: "left", + sdl2.SDL_CONTROLLER_BUTTON_DPAD_RIGHT: "right", +} + + +def dpad_to_hat(flags: Dict[str, bool]) -> int: + up = flags["up"] + down = flags["down"] + left = flags["left"] + right = flags["right"] + + if up and right: + return SwitchHat.TOP_RIGHT + if up and left: + return SwitchHat.TOP_LEFT + if down and right: + return SwitchHat.BOTTOM_RIGHT + if down and left: + return SwitchHat.BOTTOM_LEFT + if up: + return SwitchHat.TOP + if down: + return SwitchHat.BOTTOM + if right: + return SwitchHat.RIGHT + if left: + return SwitchHat.LEFT + return SwitchHat.CENTER + + +def is_usb_serial(path: str) -> bool: + if path.startswith("/dev/tty.") and not path.startswith("/dev/tty.usb"): + return False + if path.startswith("/dev/cu.") and not path.startswith("/dev/cu.usb"): + return False + return True + + +def discover_ports(include_non_usb: bool = False) -> List[Dict[str, str]]: + results: List[Dict[str, str]] = [] + for port in list_ports.comports(): + path = port.device or "" + if not path: + continue + if not include_non_usb and not is_usb_serial(path): + continue + results.append( + { + "device": path, + "description": port.description or "Unknown", + } + ) + return results + + +def interactive_pairing( + console: Console, controller_info: Dict[int, str], ports: List[Dict[str, str]] +) -> List[Tuple[int, str]]: + available = ports.copy() + mappings: List[Tuple[int, str]] = [] + for controller_idx in controller_info: + if not available: + console.print("[bold red]No more UART devices available for pairing.[/bold red]") + break + + table = Table( + title=f"Available UART Devices for Controller {controller_idx} ({controller_info[controller_idx]})" + ) + table.add_column("Choice", justify="center") + table.add_column("Port") + table.add_column("Description") + for i, port in enumerate(available): + table.add_row(str(i), port["device"], port["description"]) + console.print(table) + choices = [str(i) for i in range(len(available))] + ["q"] + selection = Prompt.ask( + "Select UART index (or 'q' to stop pairing)", + choices=choices, + default=choices[0] if choices else "q", + ) + if selection == "q": + break + idx = int(selection) + port = available.pop(idx) + mappings.append((controller_idx, port["device"])) + console.print(f"[bold green]Paired controller {controller_idx} with {port['device']}[/bold green]") + return mappings + + +@dataclass +class SwitchReport: + buttons: int = 0 + hat: int = SwitchHat.CENTER + lx: int = 128 + ly: int = 128 + rx: int = 128 + ry: int = 128 + + def to_bytes(self) -> bytes: + return struct.pack( + " None: + self.serial = serial.Serial( + port=port, + baudrate=baudrate, + bytesize=serial.EIGHTBITS, + stopbits=serial.STOPBITS_ONE, + parity=serial.PARITY_NONE, + timeout=0.0, + write_timeout=0.0, + ) + self._buffer = bytearray() + + def send_report(self, report: SwitchReport) -> None: + self.serial.write(report.to_bytes()) + self.serial.flush() + + def read_rumble_payload(self) -> Optional[bytes]: + chunk = self.serial.read(64) # non-blocking (timeout=0) + if chunk: + self._buffer.extend(chunk) + + while True: + if RUMBLE_HEADER not in self._buffer: + self._buffer.clear() + return None + start = self._buffer.find(bytes([RUMBLE_HEADER])) + if len(self._buffer) - start < 11: + # Need more bytes. + if start > 0: + del self._buffer[:start] + return None + frame = self._buffer[start : start + 11] + checksum = sum(frame[:10]) & 0xFF + if frame[1] == RUMBLE_TYPE_RUMBLE and checksum == frame[10]: + payload = bytes(frame[2:10]) + del self._buffer[: start + 11] + return payload + # Bad frame, drop the header and continue. + del self._buffer[: start + 1] + + def close(self) -> None: + self.serial.close() + + +def decode_rumble(payload: bytes) -> Tuple[float, float]: + """Return normalized rumble amplitudes (0.0-1.0) for left/right.""" + if len(payload) < 8: + return 0.0, 0.0 + # Neutral/idle pattern used by Switch: no rumble energy. + if payload == b"\x00\x01\x40\x40\x00\x01\x40\x40": + return 0.0, 0.0 + # Rumble amp is 10 bits across bytes 0/1 and 4/5. + left_raw = ((payload[1] & 0x03) << 8) | payload[0] + right_raw = ((payload[5] & 0x03) << 8) | payload[4] + if left_raw < 8 and right_raw < 8: + return 0.0, 0.0 + left = min(max(left_raw / 1023.0, 0.0), 1.0) + right = min(max(right_raw / 1023.0, 0.0), 1.0) + return left, right + + +def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> None: + left_norm, right_norm = decode_rumble(payload) + max_norm = max(left_norm, right_norm) + # Treat small rumble as "off" to avoid idle buzz. + if max_norm < 0.40: + sdl2.SDL_GameControllerRumble(controller, 0, 0, 0) + return + # Attenuate to feel closer to a real controller; cap at ~25% strength. + scale = 0.40 + left = int(min(1.0, left_norm * scale) * 0xFFFF) + right = int(min(1.0, right_norm * scale) * 0xFFFF) + duration = 25 + sdl2.SDL_GameControllerRumble(controller, left, right, duration) + + +@dataclass +class ControllerContext: + controller: sdl2.SDL_GameController + instance_id: int + uart: PicoUART + report: SwitchReport = field(default_factory=SwitchReport) + dpad: Dict[str, bool] = field(default_factory=lambda: {"up": False, "down": False, "left": False, "right": False}) + last_trigger_state: Dict[str, bool] = field(default_factory=lambda: {"left": False, "right": False}) + last_send: float = 0.0 + stop_event: threading.Event = field(default_factory=threading.Event) + rumble_thread: Optional[threading.Thread] = None + + +def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: + controller = sdl2.SDL_GameControllerOpen(index) + if not controller: + raise RuntimeError(f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}") + joystick = sdl2.SDL_GameControllerGetJoystick(controller) + instance_id = sdl2.SDL_JoystickInstanceID(joystick) + return controller, instance_id + + +def start_rumble_listener(ctx: ControllerContext) -> threading.Thread: + def _worker() -> None: + while not ctx.stop_event.is_set(): + payload = ctx.uart.read_rumble_payload() + if not payload: + time.sleep(0.0005) # small yield to avoid busy-spin + continue + apply_rumble(ctx.controller, payload) + + thread = threading.Thread(target=_worker, name=f"rumble-{ctx.instance_id}", daemon=True) + thread.start() + return thread + + +def build_arg_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Bridge SDL2 controllers to switch-pico UART (with rumble)") + parser.add_argument( + "--map", + action="append", + type=parse_mapping, + default=[], + help="Controller mapping 'index:serial_port'. Repeat per controller.", + ) + parser.add_argument( + "--ports", + nargs="+", + help="Serial ports to auto-pair with controllers in ascending index order.", + ) + parser.add_argument("--interactive", action="store_true", help="Launch an interactive pairing UI using Rich.") + parser.add_argument("--all-ports", action="store_true", help="Include non-USB serial ports when listing devices.") + parser.add_argument( + "--frequency", + type=float, + default=500.0, + help="Report send frequency per controller (Hz, default 500)", + ) + parser.add_argument("--deadzone", type=float, default=0.08, help="Stick deadzone (0.0-1.0, default 0.08)") + parser.add_argument( + "--trigger-threshold", + type=float, + default=0.35, + help="Trigger threshold treated as a digital press (0.0-1.0, default 0.35)", + ) + return parser + + +def main() -> None: + parser = build_arg_parser() + args = parser.parse_args() + interval = 1.0 / max(args.frequency, 1.0) + deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) + trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) + + sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER) + contexts: Dict[int, ControllerContext] = {} + uarts: List[PicoUART] = [] + console = Console() + try: + controller_indices: List[int] = [] + controller_names: Dict[int, str] = {} + controller_count = sdl2.SDL_NumJoysticks() + if controller_count < 0: + parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") + for index in range(controller_count): + if sdl2.SDL_IsGameController(index): + name = sdl2.SDL_GameControllerNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") + controller_indices.append(index) + controller_names[index] = name_str + + mappings = list(args.map) + if args.interactive: + if not controller_indices: + parser.error("No controllers detected for interactive pairing.") + discovered = discover_ports(include_non_usb=args.all_ports or False) + if not discovered: + parser.error("No UART devices found for interactive pairing.") + mappings = interactive_pairing(console, controller_names, discovered) + if not mappings: + parser.error("No controller-to-UART mappings were selected.") + elif not mappings: + if args.ports: + validated = list(args.ports) + if not controller_indices: + parser.error("No controllers available to pair.") + pair_count = min(len(controller_indices), len(validated)) + if pair_count == 0: + parser.error("No UART ports specified.") + if len(validated) < len(controller_indices): + console.print( + f"[yellow]Warning: only {len(validated)} UART(s) provided; pairing first {pair_count} controllers.[/yellow]" + ) + mappings = list((controller_indices[i], validated[i]) for i in range(pair_count)) + console.print("[green]Paired controllers to specified UART ports:[/green]") + for idx, port in mappings: + console.print(f" Controller {idx} -> {port}") + else: + discovered = discover_ports(include_non_usb=args.all_ports or False) + if not discovered: + parser.error("No UART devices detected automatically.") + pair_count = min(len(controller_indices), len(discovered)) + if pair_count == 0: + parser.error("No controllers detected to pair.") + if len(discovered) < len(controller_indices): + console.print( + f"[yellow]Warning: detected {len(discovered)} UART(s) but {len(controller_indices)} controller(s); pairing first {pair_count}.[/yellow]" + ) + mappings = list((controller_indices[i], discovered[i]["device"]) for i in range(pair_count)) + console.print("[green]Auto-detected UARTs:[/green]") + for info in discovered: + console.print(f" {info['device']} ({info['description']})") + console.print("[green]Paired controllers to detected UARTs:[/green]") + for idx, port in mappings: + console.print(f" Controller {idx} -> {port}") + + for index, port in mappings: + controller, instance_id = open_controller(index) + uart = PicoUART(port) + uarts.append(uart) + ctx = ControllerContext(controller=controller, instance_id=instance_id, uart=uart) + ctx.rumble_thread = start_rumble_listener(ctx) + contexts[instance_id] = ctx + console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") + + if not contexts: + parser.error("No controllers opened. Check --map/--ports/--interactive values.") + + event = sdl2.SDL_Event() + running = True + while running: + while sdl2.SDL_PollEvent(event): + if event.type == sdl2.SDL_QUIT: + running = False + break + if event.type == sdl2.SDL_CONTROLLERAXISMOTION: + ctx = contexts.get(event.caxis.which) + if not ctx: + continue + axis = event.caxis.axis + value = event.caxis.value + if axis == sdl2.SDL_CONTROLLER_AXIS_LEFTX: + ctx.report.lx = axis_to_stick(value, deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_LEFTY: + ctx.report.ly = axis_to_stick(value, deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTX: + ctx.report.rx = axis_to_stick(value, deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTY: + ctx.report.ry = axis_to_stick(value, deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERLEFT: + pressed = trigger_to_button(value, trigger_threshold) + if pressed != ctx.last_trigger_state["left"]: + if pressed: + ctx.report.buttons |= SwitchButton.ZL + else: + ctx.report.buttons &= ~SwitchButton.ZL + ctx.last_trigger_state["left"] = pressed + elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + pressed = trigger_to_button(value, trigger_threshold) + if pressed != ctx.last_trigger_state["right"]: + if pressed: + ctx.report.buttons |= SwitchButton.ZR + else: + ctx.report.buttons &= ~SwitchButton.ZR + ctx.last_trigger_state["right"] = pressed + elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): + ctx = contexts.get(event.cbutton.which) + if not ctx: + continue + button = event.cbutton.button + pressed = event.type == sdl2.SDL_CONTROLLERBUTTONDOWN + if button in BUTTON_MAP: + bit = BUTTON_MAP[button] + if pressed: + ctx.report.buttons |= bit + else: + ctx.report.buttons &= ~bit + elif button in DPAD_BUTTONS: + ctx.dpad[DPAD_BUTTONS[button]] = pressed + ctx.report.hat = dpad_to_hat(ctx.dpad) + + now = time.monotonic() + for ctx in contexts.values(): + if now - ctx.last_send >= interval: + ctx.uart.send_report(ctx.report) + ctx.last_send = now + + sdl2.SDL_Delay(1) + finally: + for ctx in contexts.values(): + ctx.stop_event.set() + if ctx.rumble_thread: + ctx.rumble_thread.join(timeout=0.2) + sdl2.SDL_GameControllerClose(ctx.controller) + for uart in uarts: + uart.close() + sdl2.SDL_Quit() + + +if __name__ == "__main__": + main() diff --git a/switch-pico.cpp b/switch-pico.cpp index 9bcdee7..881f4de 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -1,4 +1,5 @@ #include +#include #include "bsp/board.h" #include "hardware/uart.h" #include "pico/stdlib.h" @@ -13,9 +14,11 @@ // UART1 is reserved for external input frames from the host PC. #define UART_ID uart1 -#define BAUD_RATE 1000000 +#define BAUD_RATE 900000 #define UART_TX_PIN 4 #define UART_RX_PIN 5 +#define UART_RUMBLE_HEADER 0xBB +#define UART_RUMBLE_RUMBLE_TYPE 0x01 static bool g_last_mounted = false; static bool g_last_ready = false; @@ -47,6 +50,24 @@ static SwitchInputState neutral_input() { return state; } +static void send_rumble_uart_frame(const uint8_t rumble[8]) { + uint8_t frame[11]; + frame[0] = UART_RUMBLE_HEADER; + frame[1] = UART_RUMBLE_RUMBLE_TYPE; + memcpy(&frame[2], rumble, 8); + + uint8_t checksum = 0; + for (int i = 0; i < 10; ++i) { + checksum = static_cast(checksum + frame[i]); + } + frame[10] = checksum; + uart_write_blocking(UART_ID, frame, sizeof(frame)); +} + +static void on_rumble_from_switch(const uint8_t rumble[8]) { + send_rumble_uart_frame(rumble); +} + // Consume UART bytes and forward complete frames to the Switch Pro driver. static void poll_uart_frames() { static uint8_t buffer[8]; @@ -183,6 +204,7 @@ int main() { tusb_init(); switch_pro_init(); + switch_pro_set_rumble_callback(on_rumble_from_switch); g_user_state = neutral_input(); switch_pro_set_input(g_user_state); diff --git a/switch_pro_descriptors.h b/switch_pro_descriptors.h index 01d0031..44cb257 100644 --- a/switch_pro_descriptors.h +++ b/switch_pro_descriptors.h @@ -21,22 +21,21 @@ #define SWITCH_PRO_HAT_UPLEFT 0x07 #define SWITCH_PRO_HAT_NOTHING 0x08 -#define SWITCH_PRO_MASK_ZR (1U << 7) -#define SWITCH_PRO_MASK_R (1U << 6) -#define SWITCH_PRO_MASK_A (1U << 3) -#define SWITCH_PRO_MASK_B (1U << 2) -#define SWITCH_PRO_MASK_X (1U << 1) -#define SWITCH_PRO_MASK_Y 1U +#define SWITCH_PRO_MASK_Y (1U << 0) +#define SWITCH_PRO_MASK_B (1U << 1) +#define SWITCH_PRO_MASK_A (1U << 2) +#define SWITCH_PRO_MASK_X (1U << 3) +#define SWITCH_PRO_MASK_L (1U << 4) +#define SWITCH_PRO_MASK_R (1U << 5) +#define SWITCH_PRO_MASK_ZL (1U << 6) +#define SWITCH_PRO_MASK_ZR (1U << 7) -#define SWITCH_PRO_MASK_CAPTURE (1U << 5) -#define SWITCH_PRO_MASK_HOME (1U << 4) -#define SWITCH_PRO_MASK_L3 (1U << 3) -#define SWITCH_PRO_MASK_R3 (1U << 2) -#define SWITCH_PRO_MASK_PLUS (1U << 1) -#define SWITCH_PRO_MASK_MINUS 1U - -#define SWITCH_PRO_MASK_ZL (1U << 7) -#define SWITCH_PRO_MASK_L (1U << 6) +#define SWITCH_PRO_MASK_MINUS (1U << 8) +#define SWITCH_PRO_MASK_PLUS (1U << 9) +#define SWITCH_PRO_MASK_L3 (1U << 10) +#define SWITCH_PRO_MASK_R3 (1U << 11) +#define SWITCH_PRO_MASK_HOME (1U << 12) +#define SWITCH_PRO_MASK_CAPTURE (1U << 13) #define SWITCH_PRO_JOYSTICK_MIN 0x0000 #define SWITCH_PRO_JOYSTICK_MID 0x7FFF diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index 7ae9475..5e71e71 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -50,6 +50,7 @@ static uint16_t leftMaxX, leftMaxY; static uint16_t rightMinX, rightMinY; static uint16_t rightCenX, rightCenY; static uint16_t rightMaxX, rightMaxY; +static SwitchRumbleCallback rumble_callback = nullptr; static const uint8_t factory_config_data[0xEFF] = { // serial number @@ -204,6 +205,16 @@ static void read_spi_flash(uint8_t* dest, uint32_t address, uint8_t size) { } } +static void forward_rumble_to_host(const uint8_t* report, uint16_t length) { + // Output reports 0x10/0x21 include 8 rumble bytes starting at offset 2. + if (!rumble_callback || length < 10) { + return; + } + uint8_t rumble[8]; + memcpy(rumble, report + 2, sizeof(rumble)); + rumble_callback(rumble); +} + static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) { bool canSend = false; last_host_activity_ms = to_ms_since_boot(get_absolute_time()); @@ -640,6 +651,10 @@ bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchI return true; } +void switch_pro_set_rumble_callback(SwitchRumbleCallback cb) { + rumble_callback = cb; +} + bool switch_pro_is_ready() { return is_ready; } @@ -667,6 +682,9 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ uint8_t switchReportSubID = buffer[1]; LOG_PRINTF("[HID] set_report type=%d id=%u switchRID=0x%02x sub=0x%02x len=%u\n", report_type, report_id, switchReportID, switchReportSubID, bufsize); + if (switchReportID == REPORT_OUTPUT_10 || switchReportID == REPORT_OUTPUT_21) { + forward_rumble_to_host(buffer, bufsize); + } if (switchReportID == REPORT_OUTPUT_00) { // No-op, just acknowledge to clear any stalls. return; @@ -688,6 +706,9 @@ void tud_hid_report_received_cb(uint8_t instance, uint8_t report_id, uint8_t con uint8_t switchReportSubID = buffer[1]; LOG_PRINTF("[HID] report_received id=%u switchRID=0x%02x sub=0x%02x len=%u\n", report_id, switchReportID, switchReportSubID, bufsize); + if (switchReportID == REPORT_OUTPUT_10 || switchReportID == REPORT_OUTPUT_21) { + forward_rumble_to_host(buffer, bufsize); + } if (switchReportID == REPORT_OUTPUT_00) { return; } else if (switchReportID == REPORT_FEATURE) { diff --git a/switch_pro_driver.h b/switch_pro_driver.h index 79d7070..73d8582 100644 --- a/switch_pro_driver.h +++ b/switch_pro_driver.h @@ -52,3 +52,7 @@ bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchI // Driver state helpers bool switch_pro_is_ready(); + +// Optional callback fired when the host sends a rumble payload (the raw 8 rumble bytes). +typedef void (*SwitchRumbleCallback)(const uint8_t rumble_data[8]); +void switch_pro_set_rumble_callback(SwitchRumbleCallback cb); From a2646f600228b45f21221fec77d25a7c110afd37 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 22:40:36 -0700 Subject: [PATCH 014/134] WORKING --- .gitignore | 3 + host_uart_logger.py | 2 +- mac_controller_uart_bridge.py | 516 ++++++++++++++++++++++++++++++++++ switch-pico.cpp | 24 +- switch_pro_descriptors.h | 29 +- switch_pro_driver.cpp | 21 ++ switch_pro_driver.h | 4 + 7 files changed, 582 insertions(+), 17 deletions(-) create mode 100644 mac_controller_uart_bridge.py diff --git a/.gitignore b/.gitignore index d9e1db8..0c5aeec 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ Switch-Fightstick GP2040-CE build +debug +.pycache +__pycache__ !.vscode/* diff --git a/host_uart_logger.py b/host_uart_logger.py index 658f3ca..634db3a 100644 --- a/host_uart_logger.py +++ b/host_uart_logger.py @@ -12,7 +12,7 @@ except ImportError: def main(): parser = argparse.ArgumentParser(description="Read debug logs from Pico UART") parser.add_argument("-p", "--port", required=True, help="Serial port (e.g. /dev/ttyUSB0 or COM3)") - parser.add_argument("-b", "--baud", type=int, default=1000000, help="Baud rate (default: 1000000)") + parser.add_argument("-b", "--baud", type=int, default=115200, help="Baud rate (default: 115200)") args = parser.parse_args() with serial.Serial(args.port, args.baud, timeout=1) as ser: diff --git a/mac_controller_uart_bridge.py b/mac_controller_uart_bridge.py new file mode 100644 index 0000000..45bcc71 --- /dev/null +++ b/mac_controller_uart_bridge.py @@ -0,0 +1,516 @@ +#!/usr/bin/env python3 +""" +Bridge multiple SDL2 controllers to switch-pico over UART and mirror rumble back. + +The framing matches ``switch-pico.cpp``: + - Host -> Pico : 0xAA, buttons (LE16), hat, lx, ly, rx, ry + - Pico -> Host : 0xBB, 0x01, 8 rumble bytes, checksum (sum of first 10 bytes) + +Features inspired by ``host/controller_bridge.py``: + - Multiple controllers paired to multiple UART ports + - Rich-powered interactive pairing UI + - Adjustable send frequency, deadzone, and trigger thresholds + - Rumble feedback delivered to SDL2 controllers +""" + +from __future__ import annotations + +import argparse +import struct +import threading +import time +from dataclasses import dataclass, field +from typing import Dict, List, Optional, Tuple + +import serial +from serial.tools import list_ports + +import sdl2 +import sdl2.ext +from rich.console import Console +from rich.prompt import Prompt +from rich.table import Table + +UART_HEADER = 0xAA +RUMBLE_HEADER = 0xBB +RUMBLE_TYPE_RUMBLE = 0x01 +UART_BAUD = 900000 + + +class SwitchButton: + # Mirrors the masks defined in switch_pro_descriptors.h + Y = 1 << 0 + B = 1 << 1 + A = 1 << 2 + X = 1 << 3 + L = 1 << 4 + R = 1 << 5 + ZL = 1 << 6 + ZR = 1 << 7 + MINUS = 1 << 8 + PLUS = 1 << 9 + LCLICK = 1 << 10 + RCLICK = 1 << 11 + HOME = 1 << 12 + CAPTURE = 1 << 13 + + +class SwitchHat: + TOP = 0x00 + TOP_RIGHT = 0x01 + RIGHT = 0x02 + BOTTOM_RIGHT = 0x03 + BOTTOM = 0x04 + BOTTOM_LEFT = 0x05 + LEFT = 0x06 + TOP_LEFT = 0x07 + CENTER = 0x08 + + +def parse_mapping(value: str) -> Tuple[int, str]: + if ":" not in value: + raise argparse.ArgumentTypeError("Mapping must look like 'index:serial_port'") + idx_str, port = value.split(":", 1) + try: + idx = int(idx_str, 10) + except ValueError as exc: + raise argparse.ArgumentTypeError(f"Invalid controller index '{idx_str}'") from exc + if not port: + raise argparse.ArgumentTypeError("Serial port cannot be empty") + return idx, port.strip() + + +def axis_to_stick(value: int, deadzone: int) -> int: + if abs(value) < deadzone: + value = 0 + scaled = int((value + 32768) * 255 / 65535) + return max(0, min(255, scaled)) + + +def trigger_to_button(value: int, threshold: int) -> bool: + return value >= threshold + + +BUTTON_MAP = { + # Direct mapping to the Switch PRO mask definitions in switch_pro_descriptors.h + sdl2.SDL_CONTROLLER_BUTTON_A: SwitchButton.A, + sdl2.SDL_CONTROLLER_BUTTON_B: SwitchButton.B, + sdl2.SDL_CONTROLLER_BUTTON_X: SwitchButton.X, + sdl2.SDL_CONTROLLER_BUTTON_Y: SwitchButton.Y, + sdl2.SDL_CONTROLLER_BUTTON_LEFTSHOULDER: SwitchButton.L, + sdl2.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: SwitchButton.R, + sdl2.SDL_CONTROLLER_BUTTON_BACK: SwitchButton.MINUS, + sdl2.SDL_CONTROLLER_BUTTON_START: SwitchButton.PLUS, + sdl2.SDL_CONTROLLER_BUTTON_GUIDE: SwitchButton.HOME, + sdl2.SDL_CONTROLLER_BUTTON_MISC1: SwitchButton.CAPTURE, + sdl2.SDL_CONTROLLER_BUTTON_LEFTSTICK: SwitchButton.LCLICK, + sdl2.SDL_CONTROLLER_BUTTON_RIGHTSTICK: SwitchButton.RCLICK, +} + +DPAD_BUTTONS = { + sdl2.SDL_CONTROLLER_BUTTON_DPAD_UP: "up", + sdl2.SDL_CONTROLLER_BUTTON_DPAD_DOWN: "down", + sdl2.SDL_CONTROLLER_BUTTON_DPAD_LEFT: "left", + sdl2.SDL_CONTROLLER_BUTTON_DPAD_RIGHT: "right", +} + + +def dpad_to_hat(flags: Dict[str, bool]) -> int: + up = flags["up"] + down = flags["down"] + left = flags["left"] + right = flags["right"] + + if up and right: + return SwitchHat.TOP_RIGHT + if up and left: + return SwitchHat.TOP_LEFT + if down and right: + return SwitchHat.BOTTOM_RIGHT + if down and left: + return SwitchHat.BOTTOM_LEFT + if up: + return SwitchHat.TOP + if down: + return SwitchHat.BOTTOM + if right: + return SwitchHat.RIGHT + if left: + return SwitchHat.LEFT + return SwitchHat.CENTER + + +def is_usb_serial(path: str) -> bool: + if path.startswith("/dev/tty.") and not path.startswith("/dev/tty.usb"): + return False + if path.startswith("/dev/cu.") and not path.startswith("/dev/cu.usb"): + return False + return True + + +def discover_ports(include_non_usb: bool = False) -> List[Dict[str, str]]: + results: List[Dict[str, str]] = [] + for port in list_ports.comports(): + path = port.device or "" + if not path: + continue + if not include_non_usb and not is_usb_serial(path): + continue + results.append( + { + "device": path, + "description": port.description or "Unknown", + } + ) + return results + + +def interactive_pairing( + console: Console, controller_info: Dict[int, str], ports: List[Dict[str, str]] +) -> List[Tuple[int, str]]: + available = ports.copy() + mappings: List[Tuple[int, str]] = [] + for controller_idx in controller_info: + if not available: + console.print("[bold red]No more UART devices available for pairing.[/bold red]") + break + + table = Table( + title=f"Available UART Devices for Controller {controller_idx} ({controller_info[controller_idx]})" + ) + table.add_column("Choice", justify="center") + table.add_column("Port") + table.add_column("Description") + for i, port in enumerate(available): + table.add_row(str(i), port["device"], port["description"]) + console.print(table) + choices = [str(i) for i in range(len(available))] + ["q"] + selection = Prompt.ask( + "Select UART index (or 'q' to stop pairing)", + choices=choices, + default=choices[0] if choices else "q", + ) + if selection == "q": + break + idx = int(selection) + port = available.pop(idx) + mappings.append((controller_idx, port["device"])) + console.print(f"[bold green]Paired controller {controller_idx} with {port['device']}[/bold green]") + return mappings + + +@dataclass +class SwitchReport: + buttons: int = 0 + hat: int = SwitchHat.CENTER + lx: int = 128 + ly: int = 128 + rx: int = 128 + ry: int = 128 + + def to_bytes(self) -> bytes: + return struct.pack( + " None: + self.serial = serial.Serial( + port=port, + baudrate=baudrate, + bytesize=serial.EIGHTBITS, + stopbits=serial.STOPBITS_ONE, + parity=serial.PARITY_NONE, + timeout=0.0, + write_timeout=0.0, + ) + self._buffer = bytearray() + + def send_report(self, report: SwitchReport) -> None: + self.serial.write(report.to_bytes()) + self.serial.flush() + + def read_rumble_payload(self) -> Optional[bytes]: + chunk = self.serial.read(64) # non-blocking (timeout=0) + if chunk: + self._buffer.extend(chunk) + + while True: + if RUMBLE_HEADER not in self._buffer: + self._buffer.clear() + return None + start = self._buffer.find(bytes([RUMBLE_HEADER])) + if len(self._buffer) - start < 11: + # Need more bytes. + if start > 0: + del self._buffer[:start] + return None + frame = self._buffer[start : start + 11] + checksum = sum(frame[:10]) & 0xFF + if frame[1] == RUMBLE_TYPE_RUMBLE and checksum == frame[10]: + payload = bytes(frame[2:10]) + del self._buffer[: start + 11] + return payload + # Bad frame, drop the header and continue. + del self._buffer[: start + 1] + + def close(self) -> None: + self.serial.close() + + +def decode_rumble(payload: bytes) -> Tuple[float, float]: + """Return normalized rumble amplitudes (0.0-1.0) for left/right.""" + if len(payload) < 8: + return 0.0, 0.0 + # Neutral/idle pattern used by Switch: no rumble energy. + if payload == b"\x00\x01\x40\x40\x00\x01\x40\x40": + return 0.0, 0.0 + # Rumble amp is 10 bits across bytes 0/1 and 4/5. + left_raw = ((payload[1] & 0x03) << 8) | payload[0] + right_raw = ((payload[5] & 0x03) << 8) | payload[4] + if left_raw < 8 and right_raw < 8: + return 0.0, 0.0 + left = min(max(left_raw / 1023.0, 0.0), 1.0) + right = min(max(right_raw / 1023.0, 0.0), 1.0) + return left, right + + +def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> None: + left_norm, right_norm = decode_rumble(payload) + max_norm = max(left_norm, right_norm) + # Treat small rumble as "off" to avoid idle buzz. + if max_norm < 0.40: + sdl2.SDL_GameControllerRumble(controller, 0, 0, 0) + return + # Attenuate to feel closer to a real controller; cap at ~25% strength. + scale = 0.40 + left = int(min(1.0, left_norm * scale) * 0xFFFF) + right = int(min(1.0, right_norm * scale) * 0xFFFF) + duration = 25 + sdl2.SDL_GameControllerRumble(controller, left, right, duration) + + +@dataclass +class ControllerContext: + controller: sdl2.SDL_GameController + instance_id: int + uart: PicoUART + report: SwitchReport = field(default_factory=SwitchReport) + dpad: Dict[str, bool] = field(default_factory=lambda: {"up": False, "down": False, "left": False, "right": False}) + last_trigger_state: Dict[str, bool] = field(default_factory=lambda: {"left": False, "right": False}) + last_send: float = 0.0 + stop_event: threading.Event = field(default_factory=threading.Event) + rumble_thread: Optional[threading.Thread] = None + + +def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: + controller = sdl2.SDL_GameControllerOpen(index) + if not controller: + raise RuntimeError(f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}") + joystick = sdl2.SDL_GameControllerGetJoystick(controller) + instance_id = sdl2.SDL_JoystickInstanceID(joystick) + return controller, instance_id + + +def start_rumble_listener(ctx: ControllerContext) -> threading.Thread: + def _worker() -> None: + while not ctx.stop_event.is_set(): + payload = ctx.uart.read_rumble_payload() + if not payload: + time.sleep(0.0005) # small yield to avoid busy-spin + continue + apply_rumble(ctx.controller, payload) + + thread = threading.Thread(target=_worker, name=f"rumble-{ctx.instance_id}", daemon=True) + thread.start() + return thread + + +def build_arg_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Bridge SDL2 controllers to switch-pico UART (with rumble)") + parser.add_argument( + "--map", + action="append", + type=parse_mapping, + default=[], + help="Controller mapping 'index:serial_port'. Repeat per controller.", + ) + parser.add_argument( + "--ports", + nargs="+", + help="Serial ports to auto-pair with controllers in ascending index order.", + ) + parser.add_argument("--interactive", action="store_true", help="Launch an interactive pairing UI using Rich.") + parser.add_argument("--all-ports", action="store_true", help="Include non-USB serial ports when listing devices.") + parser.add_argument( + "--frequency", + type=float, + default=500.0, + help="Report send frequency per controller (Hz, default 500)", + ) + parser.add_argument("--deadzone", type=float, default=0.08, help="Stick deadzone (0.0-1.0, default 0.08)") + parser.add_argument( + "--trigger-threshold", + type=float, + default=0.35, + help="Trigger threshold treated as a digital press (0.0-1.0, default 0.35)", + ) + return parser + + +def main() -> None: + parser = build_arg_parser() + args = parser.parse_args() + interval = 1.0 / max(args.frequency, 1.0) + deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) + trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) + + sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER) + contexts: Dict[int, ControllerContext] = {} + uarts: List[PicoUART] = [] + console = Console() + try: + controller_indices: List[int] = [] + controller_names: Dict[int, str] = {} + controller_count = sdl2.SDL_NumJoysticks() + if controller_count < 0: + parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") + for index in range(controller_count): + if sdl2.SDL_IsGameController(index): + name = sdl2.SDL_GameControllerNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") + controller_indices.append(index) + controller_names[index] = name_str + + mappings = list(args.map) + if args.interactive: + if not controller_indices: + parser.error("No controllers detected for interactive pairing.") + discovered = discover_ports(include_non_usb=args.all_ports or False) + if not discovered: + parser.error("No UART devices found for interactive pairing.") + mappings = interactive_pairing(console, controller_names, discovered) + if not mappings: + parser.error("No controller-to-UART mappings were selected.") + elif not mappings: + if args.ports: + validated = list(args.ports) + if not controller_indices: + parser.error("No controllers available to pair.") + pair_count = min(len(controller_indices), len(validated)) + if pair_count == 0: + parser.error("No UART ports specified.") + if len(validated) < len(controller_indices): + console.print( + f"[yellow]Warning: only {len(validated)} UART(s) provided; pairing first {pair_count} controllers.[/yellow]" + ) + mappings = list((controller_indices[i], validated[i]) for i in range(pair_count)) + console.print("[green]Paired controllers to specified UART ports:[/green]") + for idx, port in mappings: + console.print(f" Controller {idx} -> {port}") + else: + discovered = discover_ports(include_non_usb=args.all_ports or False) + if not discovered: + parser.error("No UART devices detected automatically.") + pair_count = min(len(controller_indices), len(discovered)) + if pair_count == 0: + parser.error("No controllers detected to pair.") + if len(discovered) < len(controller_indices): + console.print( + f"[yellow]Warning: detected {len(discovered)} UART(s) but {len(controller_indices)} controller(s); pairing first {pair_count}.[/yellow]" + ) + mappings = list((controller_indices[i], discovered[i]["device"]) for i in range(pair_count)) + console.print("[green]Auto-detected UARTs:[/green]") + for info in discovered: + console.print(f" {info['device']} ({info['description']})") + console.print("[green]Paired controllers to detected UARTs:[/green]") + for idx, port in mappings: + console.print(f" Controller {idx} -> {port}") + + for index, port in mappings: + controller, instance_id = open_controller(index) + uart = PicoUART(port) + uarts.append(uart) + ctx = ControllerContext(controller=controller, instance_id=instance_id, uart=uart) + ctx.rumble_thread = start_rumble_listener(ctx) + contexts[instance_id] = ctx + console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") + + if not contexts: + parser.error("No controllers opened. Check --map/--ports/--interactive values.") + + event = sdl2.SDL_Event() + running = True + while running: + while sdl2.SDL_PollEvent(event): + if event.type == sdl2.SDL_QUIT: + running = False + break + if event.type == sdl2.SDL_CONTROLLERAXISMOTION: + ctx = contexts.get(event.caxis.which) + if not ctx: + continue + axis = event.caxis.axis + value = event.caxis.value + if axis == sdl2.SDL_CONTROLLER_AXIS_LEFTX: + ctx.report.lx = axis_to_stick(value, deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_LEFTY: + ctx.report.ly = axis_to_stick(value, deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTX: + ctx.report.rx = axis_to_stick(value, deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTY: + ctx.report.ry = axis_to_stick(value, deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERLEFT: + pressed = trigger_to_button(value, trigger_threshold) + if pressed != ctx.last_trigger_state["left"]: + if pressed: + ctx.report.buttons |= SwitchButton.ZL + else: + ctx.report.buttons &= ~SwitchButton.ZL + ctx.last_trigger_state["left"] = pressed + elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + pressed = trigger_to_button(value, trigger_threshold) + if pressed != ctx.last_trigger_state["right"]: + if pressed: + ctx.report.buttons |= SwitchButton.ZR + else: + ctx.report.buttons &= ~SwitchButton.ZR + ctx.last_trigger_state["right"] = pressed + elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): + ctx = contexts.get(event.cbutton.which) + if not ctx: + continue + button = event.cbutton.button + pressed = event.type == sdl2.SDL_CONTROLLERBUTTONDOWN + if button in BUTTON_MAP: + bit = BUTTON_MAP[button] + if pressed: + ctx.report.buttons |= bit + else: + ctx.report.buttons &= ~bit + elif button in DPAD_BUTTONS: + ctx.dpad[DPAD_BUTTONS[button]] = pressed + ctx.report.hat = dpad_to_hat(ctx.dpad) + + now = time.monotonic() + for ctx in contexts.values(): + if now - ctx.last_send >= interval: + ctx.uart.send_report(ctx.report) + ctx.last_send = now + + sdl2.SDL_Delay(1) + finally: + for ctx in contexts.values(): + ctx.stop_event.set() + if ctx.rumble_thread: + ctx.rumble_thread.join(timeout=0.2) + sdl2.SDL_GameControllerClose(ctx.controller) + for uart in uarts: + uart.close() + sdl2.SDL_Quit() + + +if __name__ == "__main__": + main() diff --git a/switch-pico.cpp b/switch-pico.cpp index 9bcdee7..881f4de 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -1,4 +1,5 @@ #include +#include #include "bsp/board.h" #include "hardware/uart.h" #include "pico/stdlib.h" @@ -13,9 +14,11 @@ // UART1 is reserved for external input frames from the host PC. #define UART_ID uart1 -#define BAUD_RATE 1000000 +#define BAUD_RATE 900000 #define UART_TX_PIN 4 #define UART_RX_PIN 5 +#define UART_RUMBLE_HEADER 0xBB +#define UART_RUMBLE_RUMBLE_TYPE 0x01 static bool g_last_mounted = false; static bool g_last_ready = false; @@ -47,6 +50,24 @@ static SwitchInputState neutral_input() { return state; } +static void send_rumble_uart_frame(const uint8_t rumble[8]) { + uint8_t frame[11]; + frame[0] = UART_RUMBLE_HEADER; + frame[1] = UART_RUMBLE_RUMBLE_TYPE; + memcpy(&frame[2], rumble, 8); + + uint8_t checksum = 0; + for (int i = 0; i < 10; ++i) { + checksum = static_cast(checksum + frame[i]); + } + frame[10] = checksum; + uart_write_blocking(UART_ID, frame, sizeof(frame)); +} + +static void on_rumble_from_switch(const uint8_t rumble[8]) { + send_rumble_uart_frame(rumble); +} + // Consume UART bytes and forward complete frames to the Switch Pro driver. static void poll_uart_frames() { static uint8_t buffer[8]; @@ -183,6 +204,7 @@ int main() { tusb_init(); switch_pro_init(); + switch_pro_set_rumble_callback(on_rumble_from_switch); g_user_state = neutral_input(); switch_pro_set_input(g_user_state); diff --git a/switch_pro_descriptors.h b/switch_pro_descriptors.h index 01d0031..44cb257 100644 --- a/switch_pro_descriptors.h +++ b/switch_pro_descriptors.h @@ -21,22 +21,21 @@ #define SWITCH_PRO_HAT_UPLEFT 0x07 #define SWITCH_PRO_HAT_NOTHING 0x08 -#define SWITCH_PRO_MASK_ZR (1U << 7) -#define SWITCH_PRO_MASK_R (1U << 6) -#define SWITCH_PRO_MASK_A (1U << 3) -#define SWITCH_PRO_MASK_B (1U << 2) -#define SWITCH_PRO_MASK_X (1U << 1) -#define SWITCH_PRO_MASK_Y 1U +#define SWITCH_PRO_MASK_Y (1U << 0) +#define SWITCH_PRO_MASK_B (1U << 1) +#define SWITCH_PRO_MASK_A (1U << 2) +#define SWITCH_PRO_MASK_X (1U << 3) +#define SWITCH_PRO_MASK_L (1U << 4) +#define SWITCH_PRO_MASK_R (1U << 5) +#define SWITCH_PRO_MASK_ZL (1U << 6) +#define SWITCH_PRO_MASK_ZR (1U << 7) -#define SWITCH_PRO_MASK_CAPTURE (1U << 5) -#define SWITCH_PRO_MASK_HOME (1U << 4) -#define SWITCH_PRO_MASK_L3 (1U << 3) -#define SWITCH_PRO_MASK_R3 (1U << 2) -#define SWITCH_PRO_MASK_PLUS (1U << 1) -#define SWITCH_PRO_MASK_MINUS 1U - -#define SWITCH_PRO_MASK_ZL (1U << 7) -#define SWITCH_PRO_MASK_L (1U << 6) +#define SWITCH_PRO_MASK_MINUS (1U << 8) +#define SWITCH_PRO_MASK_PLUS (1U << 9) +#define SWITCH_PRO_MASK_L3 (1U << 10) +#define SWITCH_PRO_MASK_R3 (1U << 11) +#define SWITCH_PRO_MASK_HOME (1U << 12) +#define SWITCH_PRO_MASK_CAPTURE (1U << 13) #define SWITCH_PRO_JOYSTICK_MIN 0x0000 #define SWITCH_PRO_JOYSTICK_MID 0x7FFF diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index 7ae9475..5e71e71 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -50,6 +50,7 @@ static uint16_t leftMaxX, leftMaxY; static uint16_t rightMinX, rightMinY; static uint16_t rightCenX, rightCenY; static uint16_t rightMaxX, rightMaxY; +static SwitchRumbleCallback rumble_callback = nullptr; static const uint8_t factory_config_data[0xEFF] = { // serial number @@ -204,6 +205,16 @@ static void read_spi_flash(uint8_t* dest, uint32_t address, uint8_t size) { } } +static void forward_rumble_to_host(const uint8_t* report, uint16_t length) { + // Output reports 0x10/0x21 include 8 rumble bytes starting at offset 2. + if (!rumble_callback || length < 10) { + return; + } + uint8_t rumble[8]; + memcpy(rumble, report + 2, sizeof(rumble)); + rumble_callback(rumble); +} + static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) { bool canSend = false; last_host_activity_ms = to_ms_since_boot(get_absolute_time()); @@ -640,6 +651,10 @@ bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchI return true; } +void switch_pro_set_rumble_callback(SwitchRumbleCallback cb) { + rumble_callback = cb; +} + bool switch_pro_is_ready() { return is_ready; } @@ -667,6 +682,9 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ uint8_t switchReportSubID = buffer[1]; LOG_PRINTF("[HID] set_report type=%d id=%u switchRID=0x%02x sub=0x%02x len=%u\n", report_type, report_id, switchReportID, switchReportSubID, bufsize); + if (switchReportID == REPORT_OUTPUT_10 || switchReportID == REPORT_OUTPUT_21) { + forward_rumble_to_host(buffer, bufsize); + } if (switchReportID == REPORT_OUTPUT_00) { // No-op, just acknowledge to clear any stalls. return; @@ -688,6 +706,9 @@ void tud_hid_report_received_cb(uint8_t instance, uint8_t report_id, uint8_t con uint8_t switchReportSubID = buffer[1]; LOG_PRINTF("[HID] report_received id=%u switchRID=0x%02x sub=0x%02x len=%u\n", report_id, switchReportID, switchReportSubID, bufsize); + if (switchReportID == REPORT_OUTPUT_10 || switchReportID == REPORT_OUTPUT_21) { + forward_rumble_to_host(buffer, bufsize); + } if (switchReportID == REPORT_OUTPUT_00) { return; } else if (switchReportID == REPORT_FEATURE) { diff --git a/switch_pro_driver.h b/switch_pro_driver.h index 79d7070..73d8582 100644 --- a/switch_pro_driver.h +++ b/switch_pro_driver.h @@ -52,3 +52,7 @@ bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchI // Driver state helpers bool switch_pro_is_ready(); + +// Optional callback fired when the host sends a rumble payload (the raw 8 rumble bytes). +typedef void (*SwitchRumbleCallback)(const uint8_t rumble_data[8]); +void switch_pro_set_rumble_callback(SwitchRumbleCallback cb); From f73b9f860474a0fc5e47a7c8ae3e903a5d8b7c0d Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 22:50:14 -0700 Subject: [PATCH 015/134] Add hot plugging --- mac_controller_uart_bridge.py | 142 ++++++++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 31 deletions(-) diff --git a/mac_controller_uart_bridge.py b/mac_controller_uart_bridge.py index 45bcc71..7cad9dd 100644 --- a/mac_controller_uart_bridge.py +++ b/mac_controller_uart_bridge.py @@ -23,6 +23,7 @@ from dataclasses import dataclass, field from typing import Dict, List, Optional, Tuple import serial +from serial import SerialException from serial.tools import list_ports import sdl2 @@ -287,7 +288,7 @@ def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> None: scale = 0.40 left = int(min(1.0, left_norm * scale) * 0xFFFF) right = int(min(1.0, right_norm * scale) * 0xFFFF) - duration = 25 + duration = 10 sdl2.SDL_GameControllerRumble(controller, left, right, duration) @@ -295,13 +296,14 @@ def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> None: class ControllerContext: controller: sdl2.SDL_GameController instance_id: int - uart: PicoUART + controller_index: int + port: str + uart: Optional[PicoUART] report: SwitchReport = field(default_factory=SwitchReport) dpad: Dict[str, bool] = field(default_factory=lambda: {"up": False, "down": False, "left": False, "right": False}) last_trigger_state: Dict[str, bool] = field(default_factory=lambda: {"left": False, "right": False}) last_send: float = 0.0 - stop_event: threading.Event = field(default_factory=threading.Event) - rumble_thread: Optional[threading.Thread] = None + last_reopen_attempt: float = 0.0 def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: @@ -313,18 +315,16 @@ def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: return controller, instance_id -def start_rumble_listener(ctx: ControllerContext) -> threading.Thread: - def _worker() -> None: - while not ctx.stop_event.is_set(): - payload = ctx.uart.read_rumble_payload() - if not payload: - time.sleep(0.0005) # small yield to avoid busy-spin - continue - apply_rumble(ctx.controller, payload) +def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: + try: + return PicoUART(port, baud) + except Exception: + return None - thread = threading.Thread(target=_worker, name=f"rumble-{ctx.instance_id}", daemon=True) - thread.start() - return thread + +def start_rumble_listener(ctx: ControllerContext) -> threading.Thread: + # No-op placeholder (rumble is polled in the main loop for hotplug safety). + return None def build_arg_parser() -> argparse.ArgumentParser: @@ -356,6 +356,12 @@ def build_arg_parser() -> argparse.ArgumentParser: default=0.35, help="Trigger threshold treated as a digital press (0.0-1.0, default 0.35)", ) + parser.add_argument( + "--baud", + type=int, + default=UART_BAUD, + help=f"UART baud rate (default {UART_BAUD}; must match switch-pico firmware)", + ) return parser @@ -369,6 +375,7 @@ def main() -> None: sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER) contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] + mapping_by_index: Dict[int, str] = {} console = Console() try: controller_indices: List[int] = [] @@ -430,15 +437,33 @@ def main() -> None: console.print(f" Controller {idx} -> {port}") for index, port in mappings: - controller, instance_id = open_controller(index) - uart = PicoUART(port) - uarts.append(uart) - ctx = ControllerContext(controller=controller, instance_id=instance_id, uart=uart) - ctx.rumble_thread = start_rumble_listener(ctx) - contexts[instance_id] = ctx - console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") + mapping_by_index[index] = port - if not contexts: + # Open currently connected controllers that match the mapping. + for index, port in mappings: + if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): + continue + try: + controller, instance_id = open_controller(index) + except Exception as exc: + console.print(f"[red]Failed to open controller {index}: {exc}[/red]") + continue + uart = try_open_uart(port, args.baud) + if uart: + uarts.append(uart) + console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") + else: + console.print(f"[yellow]Controller {index} ({instance_id}) waiting for UART {port}[/yellow]") + ctx = ControllerContext( + controller=controller, + instance_id=instance_id, + controller_index=index, + port=port, + uart=uart, + ) + contexts[instance_id] = ctx + + if not contexts and not mapping_by_index: parser.error("No controllers opened. Check --map/--ports/--interactive values.") event = sdl2.SDL_Event() @@ -493,19 +518,74 @@ def main() -> None: elif button in DPAD_BUTTONS: ctx.dpad[DPAD_BUTTONS[button]] = pressed ctx.report.hat = dpad_to_hat(ctx.dpad) + elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: + idx = event.cdevice.which + port = mapping_by_index.get(idx) + if port is None: + continue + # Avoid duplicate opens for already connected instance IDs. + already = any(c.controller_index == idx for c in contexts.values()) + if already: + continue + try: + controller, instance_id = open_controller(idx) + except Exception as exc: + console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") + continue + uart = try_open_uart(port, args.baud) + if uart: + uarts.append(uart) + console.print(f"[green]Controller {idx} ({instance_id}) paired to {port}[/green]") + else: + console.print(f"[yellow]Controller {idx} ({instance_id}) waiting for UART {port}[/yellow]") + ctx = ControllerContext( + controller=controller, + instance_id=instance_id, + controller_index=idx, + port=port, + uart=uart, + ) + contexts[instance_id] = ctx + elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: + instance_id = event.cdevice.which + ctx = contexts.pop(instance_id, None) + if ctx: + console.print(f"[yellow]Controller {instance_id} removed[/yellow]") + sdl2.SDL_GameControllerClose(ctx.controller) now = time.monotonic() - for ctx in contexts.values(): - if now - ctx.last_send >= interval: - ctx.uart.send_report(ctx.report) - ctx.last_send = now - + for ctx in list(contexts.values()): + # Reconnect UART if needed. + if ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: + ctx.last_reopen_attempt = now + uart = try_open_uart(ctx.port, args.baud) + if uart: + uarts.append(uart) + console.print(f"[green]Reconnected UART {ctx.port} for controller {ctx.controller_index}[/green]") + ctx.uart = uart + if ctx.uart is None: + continue + try: + if now - ctx.last_send >= interval: + ctx.uart.send_report(ctx.report) + ctx.last_send = now + # Poll rumble quickly while we have the port. + payload = ctx.uart.read_rumble_payload() + if payload: + apply_rumble(ctx.controller, payload) + except SerialException as exc: + console.print(f"[yellow]UART {ctx.port} disconnected: {exc}[/yellow]") + try: + ctx.uart.close() + except Exception: + pass + ctx.uart = None + ctx.last_reopen_attempt = now + except Exception as exc: + console.print(f"[red]UART error on {ctx.port}: {exc}[/red]") sdl2.SDL_Delay(1) finally: for ctx in contexts.values(): - ctx.stop_event.set() - if ctx.rumble_thread: - ctx.rumble_thread.join(timeout=0.2) sdl2.SDL_GameControllerClose(ctx.controller) for uart in uarts: uart.close() From 0a5cce3e80c2d5145f21d6cd1d8318d87b3057d9 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 20 Nov 2025 22:50:14 -0700 Subject: [PATCH 016/134] Add hot plugging --- mac_controller_uart_bridge.py | 142 ++++++++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 31 deletions(-) diff --git a/mac_controller_uart_bridge.py b/mac_controller_uart_bridge.py index 45bcc71..7cad9dd 100644 --- a/mac_controller_uart_bridge.py +++ b/mac_controller_uart_bridge.py @@ -23,6 +23,7 @@ from dataclasses import dataclass, field from typing import Dict, List, Optional, Tuple import serial +from serial import SerialException from serial.tools import list_ports import sdl2 @@ -287,7 +288,7 @@ def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> None: scale = 0.40 left = int(min(1.0, left_norm * scale) * 0xFFFF) right = int(min(1.0, right_norm * scale) * 0xFFFF) - duration = 25 + duration = 10 sdl2.SDL_GameControllerRumble(controller, left, right, duration) @@ -295,13 +296,14 @@ def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> None: class ControllerContext: controller: sdl2.SDL_GameController instance_id: int - uart: PicoUART + controller_index: int + port: str + uart: Optional[PicoUART] report: SwitchReport = field(default_factory=SwitchReport) dpad: Dict[str, bool] = field(default_factory=lambda: {"up": False, "down": False, "left": False, "right": False}) last_trigger_state: Dict[str, bool] = field(default_factory=lambda: {"left": False, "right": False}) last_send: float = 0.0 - stop_event: threading.Event = field(default_factory=threading.Event) - rumble_thread: Optional[threading.Thread] = None + last_reopen_attempt: float = 0.0 def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: @@ -313,18 +315,16 @@ def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: return controller, instance_id -def start_rumble_listener(ctx: ControllerContext) -> threading.Thread: - def _worker() -> None: - while not ctx.stop_event.is_set(): - payload = ctx.uart.read_rumble_payload() - if not payload: - time.sleep(0.0005) # small yield to avoid busy-spin - continue - apply_rumble(ctx.controller, payload) +def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: + try: + return PicoUART(port, baud) + except Exception: + return None - thread = threading.Thread(target=_worker, name=f"rumble-{ctx.instance_id}", daemon=True) - thread.start() - return thread + +def start_rumble_listener(ctx: ControllerContext) -> threading.Thread: + # No-op placeholder (rumble is polled in the main loop for hotplug safety). + return None def build_arg_parser() -> argparse.ArgumentParser: @@ -356,6 +356,12 @@ def build_arg_parser() -> argparse.ArgumentParser: default=0.35, help="Trigger threshold treated as a digital press (0.0-1.0, default 0.35)", ) + parser.add_argument( + "--baud", + type=int, + default=UART_BAUD, + help=f"UART baud rate (default {UART_BAUD}; must match switch-pico firmware)", + ) return parser @@ -369,6 +375,7 @@ def main() -> None: sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER) contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] + mapping_by_index: Dict[int, str] = {} console = Console() try: controller_indices: List[int] = [] @@ -430,15 +437,33 @@ def main() -> None: console.print(f" Controller {idx} -> {port}") for index, port in mappings: - controller, instance_id = open_controller(index) - uart = PicoUART(port) - uarts.append(uart) - ctx = ControllerContext(controller=controller, instance_id=instance_id, uart=uart) - ctx.rumble_thread = start_rumble_listener(ctx) - contexts[instance_id] = ctx - console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") + mapping_by_index[index] = port - if not contexts: + # Open currently connected controllers that match the mapping. + for index, port in mappings: + if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): + continue + try: + controller, instance_id = open_controller(index) + except Exception as exc: + console.print(f"[red]Failed to open controller {index}: {exc}[/red]") + continue + uart = try_open_uart(port, args.baud) + if uart: + uarts.append(uart) + console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") + else: + console.print(f"[yellow]Controller {index} ({instance_id}) waiting for UART {port}[/yellow]") + ctx = ControllerContext( + controller=controller, + instance_id=instance_id, + controller_index=index, + port=port, + uart=uart, + ) + contexts[instance_id] = ctx + + if not contexts and not mapping_by_index: parser.error("No controllers opened. Check --map/--ports/--interactive values.") event = sdl2.SDL_Event() @@ -493,19 +518,74 @@ def main() -> None: elif button in DPAD_BUTTONS: ctx.dpad[DPAD_BUTTONS[button]] = pressed ctx.report.hat = dpad_to_hat(ctx.dpad) + elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: + idx = event.cdevice.which + port = mapping_by_index.get(idx) + if port is None: + continue + # Avoid duplicate opens for already connected instance IDs. + already = any(c.controller_index == idx for c in contexts.values()) + if already: + continue + try: + controller, instance_id = open_controller(idx) + except Exception as exc: + console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") + continue + uart = try_open_uart(port, args.baud) + if uart: + uarts.append(uart) + console.print(f"[green]Controller {idx} ({instance_id}) paired to {port}[/green]") + else: + console.print(f"[yellow]Controller {idx} ({instance_id}) waiting for UART {port}[/yellow]") + ctx = ControllerContext( + controller=controller, + instance_id=instance_id, + controller_index=idx, + port=port, + uart=uart, + ) + contexts[instance_id] = ctx + elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: + instance_id = event.cdevice.which + ctx = contexts.pop(instance_id, None) + if ctx: + console.print(f"[yellow]Controller {instance_id} removed[/yellow]") + sdl2.SDL_GameControllerClose(ctx.controller) now = time.monotonic() - for ctx in contexts.values(): - if now - ctx.last_send >= interval: - ctx.uart.send_report(ctx.report) - ctx.last_send = now - + for ctx in list(contexts.values()): + # Reconnect UART if needed. + if ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: + ctx.last_reopen_attempt = now + uart = try_open_uart(ctx.port, args.baud) + if uart: + uarts.append(uart) + console.print(f"[green]Reconnected UART {ctx.port} for controller {ctx.controller_index}[/green]") + ctx.uart = uart + if ctx.uart is None: + continue + try: + if now - ctx.last_send >= interval: + ctx.uart.send_report(ctx.report) + ctx.last_send = now + # Poll rumble quickly while we have the port. + payload = ctx.uart.read_rumble_payload() + if payload: + apply_rumble(ctx.controller, payload) + except SerialException as exc: + console.print(f"[yellow]UART {ctx.port} disconnected: {exc}[/yellow]") + try: + ctx.uart.close() + except Exception: + pass + ctx.uart = None + ctx.last_reopen_attempt = now + except Exception as exc: + console.print(f"[red]UART error on {ctx.port}: {exc}[/red]") sdl2.SDL_Delay(1) finally: for ctx in contexts.values(): - ctx.stop_event.set() - if ctx.rumble_thread: - ctx.rumble_thread.join(timeout=0.2) sdl2.SDL_GameControllerClose(ctx.controller) for uart in uarts: uart.close() From ccbbf9c3cf630a7926309f58d5c5d56ec6392335 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 21 Nov 2025 08:11:24 -0700 Subject: [PATCH 017/134] Make other controllers work --- controller_db/gamecontrollerdb.txt | 2197 ++++++++++++++++++++++++++++ mac_controller_uart_bridge.py | 35 +- 2 files changed, 2231 insertions(+), 1 deletion(-) create mode 100644 controller_db/gamecontrollerdb.txt diff --git a/controller_db/gamecontrollerdb.txt b/controller_db/gamecontrollerdb.txt new file mode 100644 index 0000000..a288a13 --- /dev/null +++ b/controller_db/gamecontrollerdb.txt @@ -0,0 +1,2197 @@ +# Game Controller DB for SDL in 2.0.16 format +# Source: https://github.com/mdqinc/SDL_GameControllerDB + +# Windows +03000000300f00000a01000000000000,3 In 1 Conversion 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:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, +03000000fa190000918d000000000000,3 In 1 Conversion 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:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, +03000000fa2d00000100000000000000,3dRudder Foot Motion Controller,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows, +03000000d0160000040d000000000000,4Play Adapter,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 Adapter,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 Adapter,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 Adapter,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 Adapter,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, +03000000c82d00001930000000000000,8BitDo 64,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,righttrigger:b9,rightx:a3,righty:a4,start:b11,platform:Windows, +03000000c82d00000031000000000000,8BitDo Adapter,a:b0,b:b1,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:b3,y:b4,platform:Windows, +03000000c82d00000531000000000000,8BitDo Adapter 2,a:b0,b:b1,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:b3,y:b4,platform:Windows, +03000000c82d00000951000000000000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows, +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, +03000000c82d00006a28000000000000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Windows, +03000000c82d00001251000000000000,8BitDo Lite 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, +03000000c82d00001151000000000000,8BitDo Lite SE,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, +03000000c82d00000150000000000000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000151000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00005106000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,guide:b2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00002090000000000000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00000451000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows, +03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +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, +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, +03000000c82d00006928000000000000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,platform:Windows, +03000000c82d00002590000000000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +030000003512000012ab000000000000,8BitDo NES30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Windows, +03000000c82d000012ab000000000000,8BitDo NES30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000022000000090000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +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:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00000851000000000000,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:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,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, +03000000c82d00000361000000000000,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, +03000000c82d00000660000000000000,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, +03000000c82d00000960000000000000,8BitDo Pro 3,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,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000131000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000231000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000331000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000431000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00002867000000000000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b10,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, +03000000c82d00000060000000000000,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:Windows, +03000000c82d00000061000000000000,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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000102800000900000000000000,8BitDo SFC30,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, +03000000c82d000021ab000000000000,8BitDo SFC30,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, +03000000c82d00003028000000000000,8BitDo SFC30,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, +030000003512000020ab000000000000,8BitDo SN30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000030000000000000,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,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a2,rightshoulder:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00001290000000000000,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, +03000000c82d000020ab000000000000,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, +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, +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, +03000000c82d00000260000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000261000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001260000000000000,8BitDo Ultimate 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001b30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001d30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001730000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001130000000000000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001330000000000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00000121000000000000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000a00500003232000000000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +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, +03000000d1180000402c000000000000,ADT1,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a3,rightx:a2,righty:a5,x:b3,y:b4,platform:Windows, +030000006f0e00008801000000000000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00001401000000000000,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, +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, +030000006f0e00001301000000000000,Afterglow 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, +030000006f0e00001302000000000000,Afterglow 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, +030000006f0e00001304000000000000,Afterglow 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, +030000006f0e00001413000000000000,Afterglow Xbox 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, +030000006f0e00003901000000000000,Afterglow 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, +03000000ab1200000103000000000000,Afterglow 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:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b000000f9000000000000,Afterglow 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, +03000000100000008200000000000000,Akishop Customs PS360,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, +030000007c1800000006000000000000,Alienware Dual Compatible PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,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, +0300000008100000e501000000000000,Anbernic Game Pad,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000020500000913000000000000,Anbernic RG P01,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:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000373500000710000000000000,Anbernic RG P01,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, +03000000373500004610000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000190e00000110000000000000,Aquaplus Piece,a:b1,b:b0,back:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start: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, +03000000490b00004406000000000000,ASCII Seamic Controller,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, +03000000869800002500000000000000,Astro C40 TR 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, +03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-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, +03000000050b00000579000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000050b00000679000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,start:b3,platform:Windows, +03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, +03000000380800001889000000000000,AtGames Legends Gamer Pro,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b14,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000008a3500000102000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, +030000008a3500000201000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, +030000008a3500000302000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, +030000008a3500000402000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, +03000000e4150000103f000000000000,Batarang,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000d6200000e557000000000000,Batarang PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, +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,touchpad:b13,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,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, +03000000790000000700000000000000,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:a4,start:b9,x:b3,y:b0,platform:Windows, +03000000808300000300000000000000,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:a4,start:b9,x:b3,y:b0,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, +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, +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, +03000000120c0000200e000000000000,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,touchpad:b13,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,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000f10e000000000000,Brook PS2 Adapter,a:b1,b:b2,back:b13,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, +03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,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, +030000005a1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows, +030000005b1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows, +030000005b1c00002500000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,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, +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,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 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,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, +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, +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 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, +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, +03000000bc2000000091000000000000,EasySMX 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, +030000006e0500000a20000000000000,Elecom DUX60 MMO,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, +03000095090000010000000000000000,Elecom JC-U609,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows, +0300004112000000e500000000000000,Elecom JC-U909Z,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows, +03000041120000001050000000000000,Elecom JC-U911,a:b1,b:b2,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b0,x:b4,y:b5,platform:Windows, +030000006e0500000520000000000000,Elecom P301U PlayStation Controller Adapter,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, +03000000250900000218000000000000,Elecom PlayStation 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: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,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, +030000007d0400000640000000000000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,platform: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, +03000000062000001801000000000000,EMS TrioLinker Plus II,a:b0,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:b2,y:b3,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 Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000006f0e00008401000000000000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00008101000000000000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00008001000000000000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000021000000090000000000000,FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +0300000011040000c600000000000000,FC801,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, +03000000852100000201000000000000,FF GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000ad1b000028f0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b00002ef0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b000038f0000000000000,Fightpad TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, +03005036852100000000000000000000,Final Fantasy XIV Online Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000f806000001a3000000000000,Firestorm,a:b9,b:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b10,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b12,x:b8,y:b4,platform:Windows, +03000000b50700000399000000000000,Firestorm 2,a:b2,b:b4,back:b10,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,righttrigger:b9,start:b11,x:b3,y:b5,platform:Windows, +03000000b50700001302000000000000,Firestorm D3,a:b0,b:b2,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,x:b1,y:b3,platform:Windows, +03000000b40400001024000000000000,Flydigi Apex,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000151900004000000000000000,Flydigi Vader 2,a:b27,b:b26,back:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b23,leftstick:b17,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b22,rightstick:b16,righttrigger:b20,rightx:a3,righty:a4,start:b18,x:b25,y:b24,platform:Windows, +03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b14,paddle1:b4,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Windows, +03000000b40400001224000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b5,x:b2,y:b3,platform:Windows, +0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, +03000000260900002625000000000000,GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows, +03000000341a000005f7000000000000,GameCube Controller,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 Controller,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 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:Windows, +03000000790000004618000000000000,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, +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, +03000000ac0500003d03000000000000,GameSir G3,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, +03000000ac0500005b05000000000000,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, +03000000ac0500002d02000000000000,GameSir G4,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, +03000000ac0500004d04000000000000,GameSir G4,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, +03000000ac0500001a06000000000000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000373500009410000000000000,GameSir Tegenaria Lite,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, +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 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, +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,touchpad:b13,x:b0,y:b3,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,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, +030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, +03000000300f00000b01000000000000,GGE909 Recoil,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, +03000000f0250000c283000000000000,Gioteck PlayStation 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, +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, +03000000f025000031c1000000000000,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 PlayStation 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 PlayStation 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, +03000000d11800000094000000000000,Google 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, +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, +030000007d0400000840000000000000,Gravis Destroyer Tilt,+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, +030000007d0400000540000000000000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000280400000140000000000000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a3,dpup:-a4,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,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, +030000008a2e0000dd10000000000000,Hand Held Legend GC Ultimate,a:b0,b:b2,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,misc2:b24,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b1,y:b3,platform:Windows, +030000008a2e0000df10000000000000,Hand Held Legend ProGCC,a:b1,b:b0,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b3,y:b2,platform:Windows, +030000000d0f00004900000000000000,Hatsune Miku Sho 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, +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, +030000000d0f00008500000000000000,Hori 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, +030000000d0f00002500000000000000,Hori Fighting Commander 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: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,touchpad:b13,x:b0,y:b3,platform:Windows, +030000000d0f00008400000000000000,Hori 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, +030000000d0f00006201000000000000,Hori Fighting Commander Octa,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,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006401000000000000,Hori Fighting Commander Octa,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,start:b7,x:b2,y:b3,platform:Windows, +030000000d0f00005100000000000000,Hori Fighting Commander PS3,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, +030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,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 Xbox 360,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, +030000000d0f00008800000000000000,Hori Fighting Stick mini 4 PS3,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, +030000000d0f00008700000000000000,Hori Fighting Stick mini 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:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000000d0f00001000000000000000,Hori Fightstick,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, +030000000d0f00003200000000000000,Hori Fightstick 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 Fightstick 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 Fightstick 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 Fightstick 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 Fightstick 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 Fightstick 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, +030000000d0f00002700000000000000,Hori Fightstick 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, +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, +030000000d0f0000a500000000000000,Hori Miku Project Diva X HD 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:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000000d0f0000a600000000000000,Hori Miku Project Diva X HD 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,touchpad: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,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, +030000000d0f00002301000000000000,Hori 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, +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,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Windows, +030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000c900000000000000,Hori Taiko Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +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, +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,touchpad:b13,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, +030000000d0f0000ee00000000000000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000c100000000000000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000f600000000000000,Horipad Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000000d0f00000202000000000000,Horipad O Nintendo Switch 2 Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,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, +030000000d0f00009601000000000000,Horipad Steam,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,misc2:b2,paddle1:b5,paddle2:b15,paddle3:b18,paddle4:b19,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +030000000d0f0000dc00000000000000,Horipad Switch,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000242e00000b20000000000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Windows, +03000000242e0000ff0b000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Windows, +03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows, +03000000242e00006a48000000000000,Hyperkin RetroN Sq,a:b3,b:b7,back:b5,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b0,rightshoulder:b1,start:b4,x:b2,y:b6,platform:Windows, +03000000242f00000a20000000000000,Hyperkin Scout,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, +03000000242e00000a20000000000000,Hyperkin Scout Premium SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, +03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Windows, +03000000f00300008d04000000000000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:-a2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:+a5,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows, +03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000d81d00001000000000000000,iBuffalo BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +030000005c0a00000285000000000000,iDroidCon,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b6,platform:Windows, +03000000696400006964000000000000,iDroidCon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000511d00000230000000000000,iGUGU Gamecore,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b1,leftstick:b4,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b2,platform:Windows, +03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, +030000006f0e00002401000000000000,Injustice Fightstick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +03000000ef0500000300000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, +03000000fd0500000230000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows, +03000000fd0500000030000000000000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Windows, +03000000fd0500003902000000000000,InterAct 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,InterAct 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,InterAct 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, +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,+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, +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, +03000000250900000017000000000000,Joypad 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, +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, +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 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 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,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000006d04000018c2000000000000,Logitech F510,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, +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, +030000006d04000019c2000000000000,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: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, +030000006d0400001ac2000000000000,Logitech Precision,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, +030000006d0400000bc2000000000000,Logitech WingMan 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, +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, +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,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000380700008031000000000000,Mad Catz FightStick Alpha PS3,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, +030000003807000038b7000000000000,Mad Catz 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, +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,touchpad:b13,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,touchpad:b13,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, +03000000380700008232000000000000,Mad Catz PlayStation 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, +03000000380700008731000000000000,Mad Catz PlayStation 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, +030000003807000056a8000000000000,Mad Catz 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, +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,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000380700001847000000000000,Mad Catz Street Fighter 4 Xbox 360 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, +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,touchpad:b13,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, +030000008f0e00001330000000000000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000242f00003700000000000000,Mayflash F101,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000790000003018000000000000,Mayflash F300 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000242f00003900000000000000,Mayflash F300 Elite Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, +0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000242f0000f500000000000000,Mayflash N64 Adapter,a:b2,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows, +03000000242f0000f400000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows, +03000000790000007918000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,righttrigger:b7,rightx:a3,righty:a2,start:b8,platform:Windows, +030000008f0e00001030000000000000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, +03000000790000000318000000000000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, +03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000790000002418000000000000,Mega Drive Controller,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 Controller,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 Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,righttrigger:b2,start:b3,platform:Windows, +030000005e0400002800000000000000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform: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, +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 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, +030000004523000015e0000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000491900000904000000000000,Mobapad Chitu HD,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,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000ffff00000000000000000000,Mocute M053,a:b3,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b11,leftstick:b7,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b6,righttrigger:b4,rightx:a3,righty:a4,start:b8,x:b1,y:b0,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, +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, +03000000091200004488000000000000,MUSIA PlayStation 2 Input Display,a:b0,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:b11,rightx:a2,righty:a3,start:b5,x:b1,y:b3,platform:Windows, +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,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +0300000085320000170d000000000000,Nacon Revolution 5 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,touchpad:b13,x:b0,y:b3,platform:Windows, +0300000085320000190d000000000000,Nacon Revolution 5 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,touchpad:b13,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,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,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 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, +0300000092120000474e000000000000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows, +03000000921200004346000000000000,NES Controller,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-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, +03000000ec110000e1a7000000000000,Nintendo Switch,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, +030000007e0500006920000000000000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,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,misc1:b13,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, +030000007e0500007320000000000000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Windows, +030000007e0500001920000000000000,NSO 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,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Windows, +030000007e0500001720000000000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Windows, +03000000550900001472000000000000,NVIDIA Controller,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, +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,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 EX,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:a3,righty:a2,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, +03000000791d00000900000000000000,Nyko Playpad,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, +03000000782300000a10000000000000,Onlive 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, +0300000009120000072f000000000000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:-a2,leftx:a0,lefty:a1,righttrigger:-a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows, +03000000120c0000f60e000000000000,P4 Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, +03000000790000002201000000000000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +030000006f0e00008501000000000000,PDP Fightpad Pro GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000006f0e00000901000000000000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00008901000000000000,PDP Realmz Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000008f0e00004100000000000000,PlaySega,a:b1,b:b0,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b4,y:b3,platform:Windows, +03000000d620000011a7000000000000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000dd62000015a7000000000000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d620000012a7000000000000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d620000013a7000000000000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d62000003340000000000000,PowerA OPS Pro 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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000d62000002640000000000000,PowerA OPS 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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +0300000062060000d570000000000000,PowerA PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d620000014a7000000000000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006d04000084ca000000000000,Precision,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000c62400001a53000000000000,Pro Ex Mini,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +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, +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, +030000008f0e00007530000000000000,PS1 Controller,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:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000100800000300000000000000,PS2 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, +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, +03000000250900008888000000000000,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, +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, +03000000151a00006222000000000000,PS2 Dual Plus 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, +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, +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.4,dpleft:h0.8,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:b3,y:b0,platform:Windows, +03000000888800000804000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform: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, +03000000120c00000807000000000000,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, +03000000120c0000111e000000000000,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, +03000000120c0000121e000000000000,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, +03000000120c0000130e000000000000,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, +03000000120c0000150e000000000000,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, +03000000120c0000180e000000000000,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, +03000000120c0000181e000000000000,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, +03000000120c0000191e000000000000,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, +03000000120c00001e0e000000000000,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, +03000000120c0000a957000000000000,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, +03000000120c0000aa57000000000000,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, +03000000120c0000f21c000000000000,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, +03000000120c0000f31c000000000000,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, +03000000120c0000f41c000000000000,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, +03000000120c0000f51c000000000000,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, +03000000120c0000f70e000000000000,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, +03000000120e0000120c000000000000,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, +03000000160e0000120c000000000000,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, +030000001a1e0000120c000000000000,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, +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,touchpad:b13,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,touchpad:b13,x:b0,y:b3,platform:Windows, +030000004c0500005f0e000000000000,PS5 Access 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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000004c050000f20d000000000000,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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,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, +03000000222c00000025000000000000,Qanba Dragon Arcade Joystick,a: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, +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,touchpad:b13,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, +03000000ff1100004733000000000000,Ramox FPS 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,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows, +030000009b2800002300000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows, +030000009b2800006900000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows, +030000009b2800000800000000000000,Raphnet Dreamcast Adapter,a:b2,b:b1,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,lefttrigger:a2,leftx:a0,righttrigger:a3,righty:a1,start:b3,x:b10,y:b9,platform:Windows, +030000009b280000d000000000000000,Raphnet Dreamcast Adapter,a:b1,b:b0,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,lefttrigger:+a5,leftx:a0,lefty:a1,righttrigger:+a2,start:b3,x:b5,y:b4,platform:Windows, +030000009b2800006200000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, +030000009b2800003200000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, +030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, +030000009b2800001800000000000000,Raphnet Jaguar Adapter,a:b2,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b10,start:b3,x:b11,y:b12,platform:Windows, +030000009b2800003c00000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, +030000009b2800006100000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, +030000009b2800006300000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, +030000009b2800006400000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, +030000009b2800000200000000000000,Raphnet NES Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,platform:Windows, +030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,back:b5,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b9,rightx:a3,righty:a4,start:b4,x:b0,y:b3,platform:Windows, +030000009b2800004300000000000000,Raphnet Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, +030000009b2800000500000000000000,Raphnet Saturn Adapter 2.0,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, +030000009b2800000300000000000000,Raphnet SNES 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, +030000009b2800002600000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800002e00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800002f00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800005600000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800005700000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800001e00000000000000,Raphnet Vectrex 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 Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, +03000000790000008f18000000000000,Rapoo Gamepad,a:b2,b:b1,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:b3,y:b0,platform:Windows, +0300000032150000a602000000000000,Razer Huntsman V3 Pro,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b12,dpright:b13,dpup:b11,guide:b10,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, +03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000f8270000bf0b000000000000,Razer Kishi,a:b6,b:b7,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b18,leftshoulder:b12,leftstick:b19,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b13,rightstick:b20,righttrigger:b15,rightx:a3,righty:a4,start:b17,x:b9,y:b10,platform:Windows, +03000000321500000204000000000000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,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 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,touchpad:b13,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, +03000000921200004547000000000000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b6,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, +03000000632500007805000000000000,Retro Fighters 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, +0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, +03000000bd12000013d0000000000000,Retrolink Sega Saturn Classic Controller,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 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:Windows, +03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Windows, +0300000000f000000300000000000000,RetroUSB RetroPad,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, +0300000000f00000f100000000000000,RetroUSB Super RetroPort,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,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,a: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, +030000006f0e00008701000000000000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000830500007030000000000000,Rockfire Space Ranger,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b9,righttrigger:b8,start:b2,x:b3,y:b4,platform:Windows, +03000000050b0000e318000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, +03000000050b0000e518000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, +03000000050b00005819000000000000,ROG Chakram Core,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, +03000000050b0000181a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, +03000000050b00001a1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, +03000000050b00001c1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,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, +030000000d0f0000ad00000000000000,RX Gamepad,a:b0,b:b4,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b6,start:b9,x:b2,y:b1,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, +030000006f0e00001311000000000000,Saffun Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,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:b1,y:b0,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,a: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,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,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,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, +03000000a30600002106000000000000,Saitek PS1000 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000a306000020f6000000000000,Saitek PS2700 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00001101000000000000,Saitek Rumble,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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows, +030000009d0d00001130000000000000,Sanwa PlayStation Adapter,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, +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 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,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, +03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows, +03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, +03000000d804000086e6000000000000,Sega Multi Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a2,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, +0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, +0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows, +03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,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, +03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, +03000000120c00001c1e000000000000,SnakeByte 4S 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, +03000000140300000918000000000000,SNES Controller,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:Windows, +0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, +030000008f0e00000910000000000000,Sony 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, +03000000317300000100000000000000,Sony 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, +03000000666600006706000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows, +03000000e30500009605000000000000,Sony PlayStation 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:Windows, +03000000fe1400002a23000000000000,Sony PlayStation Adapter,a:b0,b:b1,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,x:b2,y:b3,platform:Windows, +030000004c050000da0c000000000000,Sony 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, +03000000632500002306000000000000,Sony PlayStation 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, +03000000f0250000c183000000000000,Sony PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d9040000160f000000000000,Sony 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, +03000000ff000000cb01000000000000,Sony PlayStation Portable,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, +030000004c0500003713000000000000,Sony 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, +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, +03000000380700001722000000000000,Speedlink Competition Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,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, +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,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, +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, +03000000381000003014000000000000,SteelSeries Stratus Duo,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, +03000000381000003114000000000000,SteelSeries Stratus Duo,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, +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, +03000000380700003847000000000000,Street Fighter Fightstick 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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, +03000000790000000418000000000000,Super Famicom Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, +03000000341200001300000000000000,Super Racer,a:b2,b:b3,back:b8,leftshoulder:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b7,x:b0,y:b1,platform:Windows, +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, +03000000e40a00000307000000000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows, +03000000e40a00000207000000000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,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, +03000000790000001c18000000000000,TGZ 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:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000790000002601000000000000,TGZ 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, +03000000591c00002400000000000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, +03000000591c00002600000000000000,THEGamepad,a:b2,b:b1,back:b6,leftx:a0,lefty:a1,start:b7,x:b3,y:b0,platform:Windows, +030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, +030000004f04000023b3000000000000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004f0400000ed0000000000000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004f04000008d0000000000000,Thrustmaster Ferrari 150 PlayStation Controller,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, +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,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,Thrustmaster Run N Drive PlayStation Controller,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,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,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, +03000000666600000288000000000000,TigerGame PlayStation 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, +03000000666600000488000000000000,TigerGame PlayStation 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,a: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, +03000000571d00002100000000000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Windows, +03000000571d00002000000000000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,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 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, +03000000151900005678000000000000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,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, +030000000b0400003065000000000000,USB 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 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 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 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 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 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 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 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 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 Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, +03000000ff1100004133000000000000,USB 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, +03000000882800000305000000000000,V5 Game Pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,x:b2,y:b3,platform:Windows, +03000000790000001a18000000000000,Venom PS4 Arcade Joystick,a: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 PS4 Arcade Joystick,a: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, +030000006f0e00000302000000000000,Victrix PS4 Pro 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,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000006f0e00000702000000000000,Victrix PS4 Pro Fightstick,a: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,touchpad:b13,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,Wii U 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, +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, +03000000380700002847000000000000,Xbox 360 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, +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, +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, +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, +030000005e040000ea02000000000000,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, +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, +030000005e040000ff02000000000000,Xbox One 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, +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 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, +03000000e0ff00000201000000000000,Xiaomi Black Shark (L),back:b0,dpdown:b11,dpleft:b9,dpright:b10,dpup:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,platform:Windows, +03000000172700004431000000000000,Xiaomi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000172700003350000000000000,Xiaomi XMGP01YM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,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,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows, +03000000c0160000e105000000000000,XinMo Dual Arcade,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, +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, +030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows, +030000002c3600000100000000000000,Yawman Arrow,+rightx:h0.2,+righty:h0.4,-rightx:h0.8,-righty:h0.1,a:b4,b:b5,back:b6,dpdown:b15,dpleft:b14,dpright:b16,dpup:b13,leftshoulder:b10,leftstick:b0,lefttrigger:-a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b12,rightshoulder:b8,rightstick:b9,righttrigger:+a4,start:b3,x:b1,y:b2,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, +03000000073500000400000000000000,Zenaim Arcade 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,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000120c00000500000000000000,Zeroplus Adapter,a:b2,b:b1,back:b11,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:b0,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, +03000000120c0000101e000000000000,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:Windows, + +# Mac OS X +030000008f0e00000300000009010000,2 In 1 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, +03000000c82d00001930000000020000,8BitDo 64,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,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X, +03000000c82d00001930000001000000,8BitDo 64,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,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X, +03000000c82d00000031000001000000,8BitDo Adapter,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:Mac OS X, +03000000c82d00000531000000020000,8BitDo Adapter 2,a:b0,b:b1,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:b3,y:b4,platform:Mac OS X, +03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X, +03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Mac OS X, +03000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001251000000020000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001151000000020000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000a30c00002400000006020000,8BitDo M30,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,guide:b9,leftshoulder:b6,lefttrigger:b5,rightshoulder:b4,righttrigger:b7,start:b8,x:b3,y:b0,platform:Mac OS X, +03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,lefttrigger:a5,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X, +03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X, +03000000c82d00002590000000010000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00002690000000010000,8BitDo NEOGEO,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b10,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +030000003512000012ab000001000000,8BitDo NES30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d000012ab000001000000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00002028000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +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, +03000000c82d00000751000000010000,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,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000660000000020000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000131000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000231000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000331000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000431000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00002867000000010000,8BitDo S30,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,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Mac OS X, +03000000c82d00003028000000010000,8Bitdo SFC30 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, +03000000102800000900000000000000,8BitDo SFC30 Joystick,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, +03000000c82d00000351000000010000,8BitDo SN30,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:Mac OS X, +03000000c82d00001290000001000000,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:Mac OS X, +03000000c82d00004028000000010000,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: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 Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001d30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001530000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001630000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001730000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001130000000020000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001330000000020000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001330000001000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000a00500003232000009010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X, +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, +0300000008100000e501000019040000,Anbernic Handheld,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:a4,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-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, +03000000050b00000579000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b42,paddle1:b9,paddle2:b11,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, +03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, +03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X, +03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X, +03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X, +030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, +030000008a3500000201000000010000,Backbone One,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, +030000008a3500000202000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, +030000008a3500000402000000010000,Backbone One,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, +030000008a3500000302000000010000,Backbone One PlayStation Edition,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,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,touchpad:b13,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,touchpad:b13,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,touchpad:b13,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 V3 Rumble Pad PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000791d00000103000009010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +030000006e0500000720000010020000,Elecom JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Mac OS X, +030000006f0e00008401000003010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000151900004000000001000000,Flydigi Vader 2,a:b14,b:b15,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Mac OS X, +03000000b40400001124000001040000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000b40400001224000003030000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X, +03000000ac0500001a06000002020000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000373500000411000023000000,GameSir X4A Xbox 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, +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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000006f0e00000102000000000000,GameStop 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, +03000000ff1100003133000007010000,GameWare PC Control Pad,a:b2,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:a3,righty:a4,start:b11,x:b3,y:b0,platform:Mac OS X, +03000000d11800000094000000010000,Google 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:Mac OS X, +030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform: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 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, +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,touchpad:b13,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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000000d0f00008400000000010000,Hori Fighting Commander 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: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,Hori Fighting Commander 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, +03000000341a00000302000014010000,Hori Fighting Stick Mini,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, +030000000d0f00008800000000010000,Hori Fighting Stick mini 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,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00008700000000010000,Hori Fighting Stick mini 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,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,touchpad:b13,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, +030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X, +030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +030000000d0f00000002000017010000,Hori Split Pad Fit,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,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, +030000000d0f0000c100000072050000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000242e0000ff0b000000010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Mac OS X, +03000000790000004e95000000010000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Mac OS X, +03000000830500006020000000000000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, +03000000ef0500000300000000020000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X, +03000000fd0500000030000010010000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Mac OS X, +030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X, +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 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, +030000006d04000019c2000000000000,Logitech Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006d04000016c2000000020000,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, +030000006d04000016c2000000030000,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, +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,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,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, +030000006d0400001fc2000000000000,Logitech F710,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,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, +03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a: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, +03000000380700005082000000010000,Mad Catz PS4 Fightpad 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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +03000000380700008483000000010000,Mad Catz PS4 Fightstick TE S Plus,a: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:Mac OS X, +0300000049190000020400001b010000,Manba One,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: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, +030000008f0e00001330000011010000,Mayflash Controller Adapter,a:b2,b:b4,back:b16,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b12,lefttrigger:b16,leftx:a0,lefty:a2,rightshoulder:b14,rightx:a6~,righty:a4,start:b18,x:b0,y:b6,platform:Mac OS X, +03000000790000004318000000010000,Mayflash GameCube Adapter,a:b4,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,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, +03000000d620000010a7000003010000,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:Mac OS X, +030000008f0e00001030000011010000,Mayflash Saturn Adapter,a:b0,b:b2,dpdown:b28,dpleft:b30,dpright:b26,dpup:b24,leftshoulder:b10,lefttrigger:b14,rightshoulder:b12,righttrigger:b4,start:b18,x:b6,y:b8,platform:Mac OS X, +0300000025090000e803000000000000,Mayflash Wii Classic Adapter,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, +03000000790000000318000000010000,Mayflash Wii DolphinBar,a:b8,b:b12,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b44,leftshoulder:b16,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b4,platform:Mac OS X, +03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, +03000000790000000018000000010000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, +030000005e0400002800000002010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Mac OS X, +030000005e0400000300000006010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Mac OS X, +030000005e0400000700000006010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Mac OS X, +030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Mac OS X, +030000004523000015e0000072050000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X, +03000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +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, +03000000853200008906000000010000,Nacon Revolution X Unlimited,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, +03000000ec110000e1a7000001010000,Nintendo Switch,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, +030000007e0500006920000001010000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,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, +030000007e0500000920000010020000,Nintendo Switch Pro 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:Mac OS X, +050000007e05000009200000ff070000,Nintendo Switch Pro 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:Mac OS X, +030000007e0500007320000001010000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Mac OS X, +030000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Mac OS X, +030000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000550900001472000025050000,NVIDIA Controller,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, +030000004b120000014d000000010000,Nyko Airflo EX,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:a3,righty:a2,start:b9,x:b2,y:b3,platform:Mac OS X, +0300000009120000072f000000010000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a2,leftx:a0,lefty:a1,righttrigger:a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Mac OS X, +030000006f0e00000901000002010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform: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, +03000000d620000011a7000000020000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000d620000011a7000010050000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, +030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, +030000004c0500006802000072050000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, +030000004c050000a00b000000010000,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:Mac OS X, +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:Mac OS X, +030000004c050000c405000000010000,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:Mac OS X, +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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +0300004b4c0500005f0e000000010000,PS5 Access 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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000e60c000000010000,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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000f20d000000010000,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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,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,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000005e040000e002000001000000,PXN P30 Pro Mobile,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:Mac OS X, +03000000222c00000225000000010000,Qanba Dragon Arcade Joystick 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, +03000000222c00000020000000010000,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,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, +030000009b2800005600000020020000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Mac OS X, +030000009b2800008000000022020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform: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 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, +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,touchpad:b13,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 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,touchpad:b13,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, +03000000632500008005000000010000,Redgear,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, +03000000632500002305000000010000,Redragon Saturn,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, +03000000921200004547000000020000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b2,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,lefttrigger:b14,rightshoulder:b10,righttrigger:b4,start:b12,x:b6,y:b8,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, +0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,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, +03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,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, +030000004c0500006802000002100000,Rii RK707,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b3,righttrigger:b9,rightx:a2,righty:a3,start:b1,x:b15,y:b12,platform:Mac OS X, +030000006f0e00008701000005010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000c6240000fefa000000000000,Rock Candy PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +03000000e804000000a000001b010000,Samsung EIGP20,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b11,leftx:a1,lefty:a3,rightshoulder:b12,rightx:a4,righty:a5,start:b16,x:b7,y:b9,platform:Mac OS X, +03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Mac OS X, +03000000a30c00002500000006020000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Mac OS X, +03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X, +03000000b40400000a01000000000000,Sega Saturn,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X, +030000003512000021ab000000000000,SFC30 Joystick,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, +0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X, +030000004c050000a00b000000000000,Sony DualShock 4 Adapter,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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000cc09000000000000,Sony DualShock 4 V2,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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +03000000666600006706000088020000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Mac OS X, +030000004c050000da0c000000010000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X, +030000004c0500003713000000010000,Sony 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, +030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +03000000110100002014000000000000,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, +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, +03000000381000003014000000000000,SteelSeries Stratus Duo,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, +03000000381000003114000000000000,SteelSeries Stratus Duo,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, +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, +030000000d0f0000f600000000010000,Switch Hori Pad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000457500002211000000010000,SZMY Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000e40a00000307000001000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X, +03000000e40a00000207000001000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X, +03000000790000001c18000000010000,TGZ 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: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, +03000000790000001c18000003100000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000591c00002400000021000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X, +03000000591c00002600000021000000,THEGamepad,a:b2,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Mac OS X, +030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X, +030000004f0400000ed0000000020000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X, +03000000571d00002100000021000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Mac OS X, +03000000bd12000015d0000000010000,Tomee Retro Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, +03000000bd12000015d0000000000000,Tomee 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:Mac OS X, +03000000571d00002000000021000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X, +030000005f140000c501000000020000,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:Mac OS X, +03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X, +03000000632500002605000000010000,Uberwith Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c0160000e105000000040000,Ultimate Atari Fight Stick,a:b2,b:b4,back:b18,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,rightshoulder:b8,righttrigger:b10,start:b16,x:b0,y:b6,platform:Mac OS X, +03000000151900005678000010010000,Uniplay U6,a:b3,b:b6,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,leftstick:b31,lefttrigger:b21,leftx:a1,lefty:a3,rightshoulder:b19,rightstick:b33,righttrigger:b23,rightx:a4,righty:a5,start:b27,x:b11,y:b13,platform:Mac OS X, +030000006f0e00000302000025040000,Victrix PS4 Pro 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,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000006f0e00000702000003060000,Victrix PS4 Pro Fightstick,a: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,touchpad:b13,x:b0,y:b3,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,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, +030000005e0400008e02000010010000,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 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 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e040000050b000003090000,Xbox Elite Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000130b000011050000,Xbox One 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 One 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, +030000005e040000200b000013050000,Xbox One 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, +030000005e040000200b000015050000,Xbox One 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, +030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e040000dd02000000000000,Xbox One 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, +030000005e040000e002000000000000,Xbox One 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 One 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, +030000005e040000e302000000000000,Xbox One 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, +030000005e040000ea02000000000000,Xbox One 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 One 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, +030000005e040000220b000013050000,Xbox One Elite 2 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,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X, +03000000c62400003a54000000000000,Xbox One PowerA 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,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000130b000013050000,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, +030000005e040000130b000015050000,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, +030000005e040000130b000007050000,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, +030000005e040000130b000017050000,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, +030000005e040000130b000022050000,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, +030000005e040000220b000017050000,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, +03000000172700004431000029010000,XiaoMi 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,a: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,a: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 +03000000c82d00001930000011010000,8BitDo 64,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,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux, +05000000c82d00001930000001000000,8BitDo 64,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,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux, +03000000c82d00000031000011010000,8BitDo Adapter,a:b0,b:b1,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:b3,y:b4,platform:Linux, +03000000c82d00000631000000010000,8BitDo Adapter 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, +03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux, +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, +05000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Linux, +03000000c82d00001251000011010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00001151000011010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000a20000000020000,8BitDo M30 Xbox,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,start:b7,x:b2,y:b3,platform:Linux, +03000000c82d00002090000011010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux, +03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00006928000011010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux, +05000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux, +05000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000008000000210000011010000,8BitDo NES30,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux, +05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux, +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, +03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00001030000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000020000000000000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +06000000c82d00000020000006010000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c82d00000960000011010000,8BitDo Pro 3,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000231000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000331000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000431000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00002867000000010000,8BitDo S30,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,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Linux, +03000000c82d00000060000011010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +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, +030000003512000012ab000010010000,8BitDo SFC30,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, +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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +05000000102800000900000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00003028000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000260000011010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000a31000014010000,8BitDo Ultimate 2C,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, +03000000c82d00001d30000011010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001130000011010000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000631000010010000,8BitDo Ultimate Wireless,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, +03000000c82d00000631000014010000,8BitDo Ultimate Wireless,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, +03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001330000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000c82d00000121000000010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, +05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00008801000011010000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00003901000013020000,Afterglow Prismatic Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00001302000000010000,Afterglow Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00003901000020060000,Afterglow 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, +03000000100000008200000011010000,Akishop Customs PS360,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 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux, +05000000491900000204000021000000,Amazon Fire 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, +05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,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:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, +0300000008100000e501000001010000,Anbernic Handheld,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Linux, +03000000020500000913000010010000,Anbernic RG P01,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, +03000000373500000710000010010000,Anbernic RG P01,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, +05000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000190e00000110000010010000,Aquaplus Piece,a:b1,b:b0,back:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b2,platform:Linux, +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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-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, +03000000050b00000579000011010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b36,paddle1:b52,paddle2:b53,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b21,paddle1:b22,paddle2:b23,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +03000000503200000110000011010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000044010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000046010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +03000000503200000210000000000000,Atari VCS Modern 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, +03000000503200000210000011010000,Atari VCS Modern 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, +05000000503200000210000000000000,Atari VCS Modern 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, +05000000503200000210000045010000,Atari VCS Modern 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, +05000000503200000210000046010000,Atari VCS Modern 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, +05000000503200000210000047010000,Atari VCS Modern 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, +030000008a3500000201000011010000,Backbone One,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, +030000008a3500000202000011010000,Backbone One,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, +030000008a3500000302000011010000,Backbone One,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, +030000008a3500000402000011010000,Backbone One,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,touchpad:b13,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 Controller 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, +03000000bc2000004d50000011010000,Beitong A1T2 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, +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, +03000000bc2000006412000011010000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b30,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, +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, +03000000120c0000300e000011010000,Brook Audio Fighting Board 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, +03000000120c0000310e000011010000,Brook Audio Fighting Board 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,touchpad:b13,x:b0,y:b3,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,touchpad:b13,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,touchpad:b13,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, +03000000d81d00000b00000010010000,Buffalo BSGP1601,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,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, +03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,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, +03000000632500007a05000001020000,Cosmic Byte Ares Wired 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: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 V3 Rumble,a: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, +030000005e0400008e02000002010000,Data Frog S80,a:b1,b:b0,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, +03000000791d00000103000010010000,Dual Box Wii Classic Adapter,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, +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, +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, +03000000242f00009100000000010000,EasySMX ESM-9101,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, +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, +030000006e0500000720000010010000,Elecom W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux, +030000007d0400000640000010010000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,platform:Linux, +03000000430b00000300000000010000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +030000006f0e00008401000011010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00008101000011010000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00008001000011010000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000852100000201000010010000,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:Linux, +05000000b40400001224000001010000,Flydigi APEX 4,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b20,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000b40400001124000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000b40400001224000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000007e0500003703000000000000,GameCube Adapter,a:b0,b:b1,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, +19000000030000000300000002030000,GameForce Controller,a:b1,b:b0,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, +03000000373500000b10000019010000,GameSir Cyclone 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, +03000000ac0500005b05000010010000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000558500001b06000010010000,GameSir G4 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000ac0500002d0200001b010000,GameSir G4s,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b33,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000ac0500007a05000011010000,GameSir G5,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000373500009710000001020000,GameSir Kaleid Flux,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000ac0500001a06000011010000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +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, +03000000373500009410000010010000,GameSir Tegenaria Lite,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, +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 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, +03000000f025000021c1000010010000,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:Linux, +03000000f0250000c283000010010000,Gioteck VX2 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, +190000004b4800000010000000010000,GO-Advance Controller,a:b1,b:b0,back:b10,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,leftshoulder:b4,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b13,start:b15,x:b2,y:b3,platform:Linux, +190000004b4800000010000001010000,GO-Advance Controller,a:b1,b:b0,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b13,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b16,righttrigger:b15,start:b17,x:b2,y:b3,platform:Linux, +190000004b4800000011000000010000,GO-Super Gamepad,a:b0,b:b1,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b3,y:b2,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, +03000000d11800000094000011010000,Google 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, +05000000d11800000094000000010000,Google 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, +0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000001010000,GPD Win Max 2 6800U Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000007d0400000540000000010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +03000000280400000140000000010000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000008f0e00000610000000010000,GreenAsia Electronics Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux, +030000008f0e00001200000010010000,GreenAsia 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, +030000008a2e0000dd10000011010000,Hand Held Legend GC Ultimate,a:b0,b:b2,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,misc2:b24,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b1,y:b3,platform:Linux, +030000008a2e0000df10000011010000,Hand Held Legend ProGCC,a:b1,b:b0,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b3,y:b2,platform:Linux, +06000000adde0000efbe000002010000,Hidromancer 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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, +03000000c9110000f055000011010000,HJC 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, +030000000d0f00006d00000020010000,Hori EDGE 301,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, +030000000d0f00008400000011010000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,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,touchpad:b13,x:b0,y:b3,platform:Linux, +030000000d0f00005001000009040000,Hori Fighting Commander Octa 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, +030000000d0f00008500000010010000,Hori Fighting Commander 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, +030000000d0f00008600000002010000,Hori Fighting Commander Xbox 360,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, +030000000d0f00003701000013010000,Hori Fighting Stick Mini,a:b1,b:b0,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:b3,y:b2,platform:Linux, +030000000d0f00008800000011010000,Hori Fighting Stick mini 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,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00008700000011010000,Hori Fighting Stick mini 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,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +030000000d0f00001000000011010000,Hori Fightstick 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, +03000000ad1b000003f5000033050000,Hori Fightstick VX,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b8,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, +030000000d0f00004d00000011010000,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: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,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, +030000000d0f00001100000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00002200000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006a00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, +030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000000d0f00008501000017010000,Hori Split Pad Fit,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, +030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,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: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, +030000000d0f0000c100000011010000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006700000001010000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000000d0f0000ab01000011010000,Horipad Steam,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,misc2:b2,paddle1:b19,paddle2:b18,paddle3:b15,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000000d0f00009601000091000000,Horipad Steam,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,misc2:b2,paddle1:b19,paddle2:b18,paddle3:b15,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000000d0f0000f600000001000000,Horipad Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000341a000005f7000010010000,HuiJia GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, +05000000242e00000b20000001000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Linux, +03000000242e0000ff0b000011010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Linux, +03000000242e00006a38000010010000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Linux, +03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, +030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,platform:Linux, +050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, +03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux, +03000000120c00000500000010010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux, +03000000ef0500000300000000010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux, +03000000fd0500000030000000010000,InterAct GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Linux, +03000000fd0500002a26000000010000,InterAct HammerHead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux, +0500000049190000020400001b010000,Ipega PG 9069,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,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,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, +03000000300f00001101000010010000,Jess Tech Colour 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, +03000000300f00001001000010010000,Jess Tech Dual Analog Rumble,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,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 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, +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:Linux, +050000007e0500000620000001000000,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:Linux, +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:Linux, +050000007e0500000720000001000000,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:Linux, +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:Linux, +03000000242f00002d00000011010000,JYS 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 Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux, +030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d040000d1ca000000000000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d040000d1ca000011010000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d0400001dc2000014400000,Logitech F310,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, +030000006d0400001ec2000019200000,Logitech F510,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, +030000006d0400001ec2000020200000,Logitech F510,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,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,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, +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, +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, +03000000380700008532000010010000,Mad Catz Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,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,touchpad:b13,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, +03000000380700008031000011010000,Mad Catz FightStick Alpha 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, +03000000380700008081000011010000,Mad Catz 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,leftstick: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, +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,touchpad:b13,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,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000380700001888000010010000,Mad Catz 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,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000380700003888000010010000,Mad Catz Joystick,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, +03000000380700001647000010040000,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, +03000000380700003847000090040000,Mad Catz 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, +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, +030000008f0e00001330000010010000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Linux, +03000000790000004318000010010000,Mayflash GameCube 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, +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, +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, +030000008f0e00001030000010010000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux, +0300000025090000e803000001010000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, +03000000790000000318000011010000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux, +03000000790000000018000011010000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000b50700001203000010010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, +03000000b50700004f00000000010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux, +03000000780000000600000010010000,Microntek Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, +030000005e0400002800000000010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Linux, +030000005e0400000300000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux, +030000005e0400000700000000010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux, +030000005e0400000e00000000010000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, +030000005e0400002700000000010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Linux, +030000005e0400008502000000010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +030000005e0400008902000021010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +030000005e0400008e02000001000000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.1,dpleft:h0.2,dpright:h0.8,dpup:h0.4,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,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, +030000005e0400008e02000056210000,Microsoft 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, +030000005e0400008e02000062230000,Microsoft 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, +030000005e040000d102000001010000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000d102000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000dd02000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000ea02000008040000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000ea0200000f050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b000009050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000e302000003020000,Microsoft Xbox One Elite,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000000b000007040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000000b000008040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e0400008e02000030110000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000016050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000017050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b000001050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, +03000000790000001c18000010010000,Mobapad Chitu HD,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, +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, +05000000e80400006e0400001b010000,Mocute 053X M59,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, +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,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,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 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, +030000005e0400008e02000010020000,MSI GC20 V2,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,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, +03000000f70600000100000000010000,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:Linux, +030000006b1400000906000014010000,Nacon Asymmetric Wireless PS4 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, +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, +03000000853200000706000012010000,Nacon GC-100,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, +05000000853200000503000000010000,Nacon MG-X Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +0300000085320000170d000011010000,Nacon Revolution 5 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,touchpad:b13,x:b0,y:b3,platform:Linux, +0300000085320000190d000011010000,Nacon Revolution 5 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,touchpad:b13,x:b0,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, +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, +0300000092120000474e000000010000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,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, +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, +03000000ec110000e1a7000010010000,Nintendo Switch,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, +030000007e0500006920000011010000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Linux, +060000004e696e74656e646f20537700,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +060000007e0500000620000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +060000007e0500000820000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +050000004c69632050726f20436f6e00,Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b16,b:b15,back:b4,leftshoulder:b6,leftstick:b12,leftx:a1,lefty:a0~,rightshoulder:b8,start:b9,x:b14,y:b17,platform:Linux, +030000007e0500000920000000026803,Nintendo Switch Pro Controller,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Linux, +030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +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,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux, +05000000010000000100000003000000,Nintendo Wii Remote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +050000007e0500003003000001000000,Nintendo Wii U Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, +050000005a1d00000218000003000000,Nokia GC 5000,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, +030000000d0500000308000010010000,Nostromo n45 Dual Analog,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux, +030000007e0500007320000011010000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Linux, +030000007e0500001920000011810000,NSO N64 Controller,+rightx:b2,+righty:b3,-rightx:b4,-righty:b10,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b7,righttrigger:b9,start:b11,platform:Linux, +050000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Linux, +050000007e0500001920000001800000,NSO N64 Controller,+rightx:b2,+righty:b3,-rightx:b4,-righty:b10,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b7,righttrigger:b9,start:b11,platform:Linux, +030000007e0500001e20000011810000,NSO Sega Genesis Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,misc1:b3,rightshoulder:b2,righttrigger:b4,start:b5,platform:Linux, +030000007e0500001720000011810000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, +050000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b8,start:b10,x:b3,y:b2,platform:Linux, +050000007e0500001720000001800000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, +03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +03000000550900001472000011010000,NVIDIA Controller,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,misc1:b8,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, +05000000550900001472000001000000,NVIDIA Controller,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,misc1:b8,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, +030000004b120000014d000000010000,NYKO Airflo EX,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:a3,righty:a2,start:b9,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,ODROID Go 2,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,ODROID Go 2,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, +05000000362800000100000002010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, +05000000362800000100000003010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, +05000000362800000100000004010000,OUYA Controller,a:b0,b:b3,back:b14,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,start:b16,x:b1,y:b2,platform:Linux, +03000000830500005020000010010000,Padix Rockfire PlayStation 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, +03000000ff1100003133000010010000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000006f0e0000b802000001010000,PDP Afterglow 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 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, +030000006f0e0000d702000006640000,PDP Black Camo Wired Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b13,dpup:b14,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00008501000011010000,PDP Fightpad Pro Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00002801000011010000,PDP PS3 Rock Candy Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00000901000011010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00002f01000011010000,PDP Wired 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, +03000000ad1b000004f9000000010000,PDP Xbox 360 Versus Fighting,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e0000f102000000000000,PDP Xbox Atomic,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, +030000006f0e0000a802000023020000,PDP Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +030000006f0e0000a702000023020000,PDP Xbox One Raven Black,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e0000d802000006640000,PDP Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e0000ef02000007640000,PDP Xbox Series Kinetic Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +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, +03000000d62000000540000001010000,PowerA Advantage 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, +03000000d620000011a7000011010000,PowerA Core 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, +03000000dd62000015a7000011010000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000d620000012a7000011010000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000d62000000140000001010000,PowerA Fusion Pro 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000c62400001a53000000010000,PowerA Mini Pro Ex,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d620000013a7000011010000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000d620000014a7000011010000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000c62400001a58000001010000,PowerA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d62000000220000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, +03000000d62000000228000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c62400001a54000001010000,PowerA Xbox One Mini 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, +03000000d62000000240000001010000,PowerA Xbox One Spectra Infinity,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d62000000520000050010000,PowerA 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, +03000000d62000000b20000001010000,PowerA 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, +03000000d62000000f20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,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, +03000000250900000017000010010000,PS/SS/N64 Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b5,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2~,righty:a3,start:b8,platform:Linux, +03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, +03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,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, +030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,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, +030000005f1400003102000010010000,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:Linux, +030000006f0e00001402000011010000,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, +030000008f0e00000300000010010000,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:Linux, +050000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,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, +050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,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, +05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +030000004c050000a00b000011010000,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:Linux, +030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +030000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +030000004c050000c405000011010000,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:Linux, +030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +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,touchpad:b13,x:b0,y:b3,platform:Linux, +030000004c050000cc09000011010000,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:Linux, +030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +03000000c01100000140000011010000,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:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +0300004b4c0500005f0e000011010000,PS5 Access 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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +030000004c050000e60c000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +030000004c050000f20d000011010000,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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +030000004c050000f20d000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +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,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +050000004c050000e60c000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +050000004c050000f20d000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +03000000300f00001211000011010000,Qanba Arcade Joystick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux, +03000000222c00000225000011010000,Qanba Dragon Arcade Joystick 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, +03000000222c00000025000011010000,Qanba Dragon Arcade Joystick 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,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000222c00001220000011010000,Qanba Drone 2 Arcade Joystick 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:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000222c00001020000011010000,Qanba Drone 2 Arcade Joystick PS5,a: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, +03000000222c00000020000011010000,Qanba Drone Arcade PS4 Joystick,a: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,rightshoulder:b5,righttrigger:a4,start:b9,touchpad:b13,x:b0,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, +03000000222c00000223000011010000,Qanba Obsidian Arcade Joystick 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, +03000000222c00000023000011010000,Qanba Obsidian Arcade Joystick 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,touchpad:b13,x:b0,y:b3,platform:Linux, +030000009b2800000300000001010000,Raphnet 4nes4snes,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, +030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux, +0300132d9b2800006500000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +0300132d9b2800006500000001010000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +030000009b2800006000000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +030000009b2800003c00000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux, +030000009b2800006100000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux, +030000009b2800006300000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux, +030000009b2800006400000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux, +030000009b2800008000000020020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux, +030000009b2800008000000001010000,Raphnet Wii Classic Adapter V3,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux, +03000000f8270000bf0b000011010000,Razer Kishi,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000321500000204000011010000,Razer Panthera PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000104000011010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000321500000810000011010000,Razer Panthera PS4 Evo Arcade Stick,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,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000321500000010000011010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +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: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 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,touchpad:b13,x:b0,y:b3,platform:Linux, +030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000321500000b10000011010000,Razer Wolverine PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +0300000032150000140a000001010000,Razer Wolverine Ultimate Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000000d0f0000c100000010010000,Retro Bit Legacy16,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b12,leftshoulder:b4,lefttrigger:b6,misc1:b13,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Linux, +0300000003040000c197000011010000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, +190000004b4800000111000000010000,RetroGame Joypad,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux, +0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, +00000000526574726f53746f6e653200,RetroStone 2 Controller,a:b1,b:b0,back:b10,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Linux, +03000000341200000400000000010000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start: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, +030000006f0e00008701000011010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +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, +030000006f0e00001311000011010000,Saffun Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,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:b1,y:b0,platform:Linux, +03000000a306000023f6000011010000,Saitek Cyborg PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, +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,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, +03000000a30600000d5f000010010000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux, +03000000a30600000c04000011010000,Saitek P2900,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,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,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, +03000000a306000020f6000011010000,Saitek PS2700 Rumble,a: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, +05000000e804000000a000001b010000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux, +03000000952e00004b43000011010000,Scuf Envision,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:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, +03000000952e00004d43000011010000,Scuf Envision,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:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, +03000000952e00004e43000011010000,Scuf Envision,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:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,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, +03000000790000001100000011010000,Sega Saturn,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,Sega Saturn,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,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux, +03000000632500002305000010010000,ShanWan 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, +03000000632500002605000010010000,ShanWan 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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000632500007505000010010000,ShanWan 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 Gamepad,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, +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, +030000004b2900000430000011000000,Snakebyte 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, +050000004c050000cc09000001000000,Sony DualShock 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,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000666600006706000000010000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux, +030000004c050000da0c000011010000,Sony 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,Sony 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, +03000000ff000000cb01000010010000,Sony PlayStation Portable,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, +030000004c0500003713000011010000,Sony 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, +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,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,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, +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, +03000000de2800000112000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:+a5,dpleft:-a4,dpright:+a4,dpup:-a5,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,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, +03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, +03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,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, +03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, +03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,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, +05000000de2800000511000001000000,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, +05000000de2800000611000001000000,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, +03000000de2800000512000010010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux, +03000000de2800000512000011010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux, +03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux, +03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000ad1b000038f0000090040000,Street Fighter IV Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000003b07000004a1000000010000,Suncom SFX Plus,a:b0,b:b2,back:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux, +030000001f08000001e4000010010000,Super Famicom Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform: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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,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, +03000000457500000401000011010000,SZMY Power DS4 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: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, +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,a: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, +03000000e40a00000307000011010000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux, +03000000e40a00000207000011010000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux, +03000000ba2200000701000001010000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Linux, +03000000790000001c18000011010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000591c00002400000010010000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux, +03000000591c00002600000010010000,THEGamepad,a:b2,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux, +030000004f04000015b3000001010000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, +030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, +030000004f04000020b3000010010000,Thrustmaster Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, +030000004f04000023b3000000010000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform: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, +030000004f04000004b3000010010000,Thrustmaster Firestorm Dual Power,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, +030000004f04000026b3000002040000,Thrustmaster GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c6240000025b000002020000,Thrustmaster GPX,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, +030000004f04000008d0000000010000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004f04000009d0000000010000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000004f04000007d0000000010000,Thrustmaster T Mini,a: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, +03000000571d00002000000010010000,Tomee SNES Adapter,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, +03000000bd12000015d0000010010000,Tomee 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, +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, +03000000680a00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux, +03000000780300000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux, +03000000e00d00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux, +03000000f00600000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux, +030000005f140000c501000010010000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +06000000f51000000870000003010000,Turtle Beach Recon,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000100800000100000010010000,Twin PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +03000000c0160000e105000010010000,Ultimate Atari Fight Stick,a:b1,b:b2,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b8,x:b0,y:b3,platform:Linux, +03000000151900005678000010010000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +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 Gamepad,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux, +03000000790000001a18000011010000,Venom PS4 Arcade Joystick,a: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, +03000000790000001b18000011010000,Venom PS4 Arcade Joystick,a: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, +030000006f0e00000302000011010000,Victrix Pro 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:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +030000006f0e00000702000011010000,Victrix Pro Fightstick 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,touchpad:b13,x:b0,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, +05000000434f4d4d414e440000000000,VX Gaming Command Series,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, +0000000058626f782033363020576900,Xbox 360 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, +030000005e0400001907000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +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, +030000005e0400009102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000a102000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000a102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000a102000030060000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00001503000000020000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000000010000,Xbox 360 EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000a102000014010000,Xbox 360 Receiver,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +0000000058626f782047616d65706100,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:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400000202000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +030000005e0400008e02000072050000,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, +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, +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, +030000005e040000ea02000000000000,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, +030000005e040000ea02000001030000,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, +050000005e040000e002000003090000,Xbox One 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 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, +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, +060000005e040000dd02000003020000,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, +050000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000005e040000ea02000011050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000ea02000015050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000ea02000017050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000ea0200000b050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000ea0200000d050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000ea02000016050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +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, +030000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000009050000,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, +030000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b00000f050000,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, +030000005e040000120b000011050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000014050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000015050000,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, +050000005e040000130b000007050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000130b000011050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000130b000017050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000005e040000130b000022050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +060000005e040000120b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000200b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000200b000023050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux, +03000000c0160000e105000001010000,XinMo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux, +030000005e0400008e02000020010000,XInput Adapter,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, +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,a: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,a: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, +03000000120c0000182e000011010000,Zeroplus 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, + +# Android +38653964633230666463343334313533,8BitDo Adapter,a:b0,b:b1,back:b15,dpdown:b12,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, +36666264316630653965636634386234,8BitDo Adapter 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,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, +38426974446f20417263616465205374,8BitDo Arcade Stick,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b5,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +61393962646434393836356631636132,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +64323139346131306233636562663738,8BitDo Arcade Stick,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +64643565386136613265663236636564,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +33313433353539306634656436353432,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38426974446f20446f67626f6e65204d,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, +34343439373236623466343934376233,8BitDo FC30 Pro,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b28,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b29,righttrigger:b7,start:b5,x:b30,y:b2,platform:Android, +38426974446f204e4743204d6f646b69,8BitDo GameCube,a:b0,b:b2,back:b4,dpdown:b12,dpleft:b13,dpright:b14,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b18,paddle2:b17,rightshoulder:b15,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b1,y:b3,platform:Android, +38426974446f2038426974446f204c69,8BitDo Lite,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +30643332373663313263316637356631,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +38426974446f204c6974652032000000,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +62656331626461363634633735353032,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +38393936616436383062666232653338,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,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:b3,y:b2,platform:Android, +38426974446f204c6974652053450000,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +39356430616562366466646636643435,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,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:b3,y:b2,platform:Android, +05000000c82d000006500000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a5,rightshoulder:b10,righttrigger:a4,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000051060000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b3,y:b2,platform:Android, +32323161363037623637326438643634,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +33656266353630643966653238646264,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,start:b10,x:b19,y:b2,platform:Android, +38426974446f204d3330204d6f646b69,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39366630663062373237616566353437,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,platform:Android, +38426974446f204d6963726f2067616d,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, +61653365323561356263373333643266,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, +62613137616239666338343866326336,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, +33663431326134333366393233616633,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, +38426974446f204e3330204d6f646b69,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, +05000000c82d000015900000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000065280000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38323035343766666239373834336637,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, +38426974446f204e3634204d6f646b69,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, +32363135613966656338666638666237,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35363534633333373639386466346631,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38426974446f204e454f47454f204750,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39383963623932353561633733306334,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000000220000000900000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +050000002038000009000000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38313433643131656262306631373166,8BitDo P30,a:b0,b:b1,back:b15,dpdown: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, +38326536643339353865323063616339,8BitDo P30,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, +38426974446f2050333020636c617373,8BitDo P30,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, +35376664343164386333616535333434,8BitDo Pro 2,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,start:b10,x:b19,y:b2,platform:Android, +38426974446f2038426974446f205072,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +38426974446f2050726f203200000000,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +61333362366131643730353063616330,8BitDo Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +62373739366537363166326238653463,8BitDo Pro 2,a:b1,b:b0,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,x:b3,y:b2,platform:Android, +38386464613034326435626130396565,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,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, +38426974446f2038426974446f205265,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,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, +66303230343038613365623964393766,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,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, +38426974446f20533330204d6f646b69,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +66316462353561376330346462316137,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,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: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:b2,y:b17,platform:Android, +61623334636338643233383735326439,8BitDo SFC30,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b31,start:b5,x:b30,y:b2,platform:Android, +05000000c82d000012900000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000062280000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +38316230613931613964356666353839,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f20534e3330204d6f646b,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +65323563303231646531383162646335,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +35383531346263653330306238353131,8BitDo SN30 PP,a:b0,b:b1,back:b15,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, +05000000c82d000001600000ffff3f00,8BitDo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000002600000ffff0f00,8BitDo SN30 Pro Plus,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, +36653638656632326235346264663661,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,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:b3,y:b2,platform:Android, +38426974446f20534e33302050726f2b,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,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, +536f6e7920436f6d707574657220456e,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b15,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, +66306331643531333230306437353936,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,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:b3,y:b2,platform:Android, +050000002028000009000000ffff3f00,8BitDo SNES30,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,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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +38426974646f205a65726f2047616d65,8BitDo Zero,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +33663434393362303033616630346337,8BitDo Zero 2,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +32333634613735616163326165323731,Amazon Luna Controller,a:b0,b:b1,back:b4,dpdown: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,x:b2,y:b3,platform:Android, +4c696e757820342e31392e3137322077,Anbernic Handheld,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Android, +417374726f2063697479206d696e6920,Astro City Mini,a:b23,b:b22,back:b29,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android, +35643263313264386134376362363435,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,start:b6,platform:Android, +32353831643566306563643065356239,Atari VCS Modern 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, +4f64696e20436f6e74726f6c6c657200,AYN Odin,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b14,dpright:b13,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, +32303165626138343962363666346165,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +38383337343564366131323064613561,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +34313430343161653665353737323365,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, +4875694a6961204a432d573031550000,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, +30363230653635633863366338623265,Evo VR,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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: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, +35383633353935396534393230616564,Google Stadia Controller,a:b0,b:b1,back:b15,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, +476f6f676c65204c4c43205374616469,Google Stadia Controller,a:b0,b:b1,back:b15,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, +5374616469614e3848532d6532633400,Google Stadia Controller,a:b0,b:b1,back:b15,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, +05000000d6020000e5890000dfff3f80,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,platform:Android, +66633030656131663837396562323935,Hori Battle,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +484f524920434f2e2c4c54442e203130,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +484f524920434f2e2c4c544420205041,Hori Gem Pad 3,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, +65656436646661313232656661616130,Hori PC Engine Mini Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b18,platform:Android, +31303433326562636431653534636633,Hori Real Arcade Pro 3,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +32656664353964393561366362333636,Hori Switch Split Pad Pro,a:b0,b:b1,back:b15,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, +30306539356238653637313730656134,HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +48797065726b696e2050616400000000,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,platform:Android, +62333331353131353034386136626636,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,platform:Android, +31306635363562663834633739396333,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +5368616e57616e202020202048797065,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b2,y:b3,platform:Android, +5553422c322d6178697320382d627574,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b3,y:b2,platform:Android, +64306137363261396266353433303531,InterAct GoPad,a:b24,b:b25,leftshoulder:b23,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,x:b21,y:b22,platform:Android, +532e542e442e20496e74657261637420,InterAct HammerHead FX,a:b23,b:b24,back:b30,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,leftstick:b22,lefttrigger:b28,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b25,righttrigger:b29,rightx:a2,righty:a3,start:b31,x:b20,y:b21,platform:Android, +65346535636333663931613264643164,Joy-Con,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, +4a6f792d436f6e20284c290000000000,Joy-Con (L),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, +38383665633039363066383334653465,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +39373064396565646338333134303131,Joy-Con (R),a:b1,b:b2,back:b5,leftstick:b8,leftx:a1~,lefty:a0,start:b6,x:b0,y:b3,platform:Android, +4a6f792d436f6e202852290000000000,Joy-Con (R),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +39656136363638323036303865326464,JYS Aapter,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,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:b0,y:b2,platform:Android, +64623163333561643339623235373232,Logitech F310,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, +35623364393661626231343866613337,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,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, +4c6f6769746563682047616d65706164,Logitech F710,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, +64396331333230326333313330336533,Logitech F710,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, +39653365373864633935383236363438,Logitech G Cloud,a:b0,b:b1,back:b15,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, +416d617a6f6e2047616d6520436f6e74,Luna Controller,a:b0,b:b1,back:b15,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, +4c756e612047616d6570616400000000,Luna 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, +30363066623539323534363639323363,Magic NS,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,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:b0,y:b2,platform:Android, +6d6179666c617368206c696d69746564,Mayflash GameCube Adapter,a:b22,b:b21,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a5,righty:a2,start:b30,x:b23,y:b24,platform:Android, +436f6e74726f6c6c6572000000000000,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +65666330633838383061313633326461,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +37316565396364386635383230353365,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +4875694a696120205553422047616d65,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +535a4d792d706f776572204c54442043,Mayflash Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b31,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, +30653962643666303631376438373532,Mayflash Wii DolphinBar,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b0,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, +39346131396233376535393665363161,Mayflash Wii U Pro Adapter,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftstick:b31,lefttrigger:b27,rightshoulder:b26,rightstick:b0,righttrigger:b28,rightx:a0,righty:a1,start:b30,x:b21,y:b24,platform:Android, +31323564663862633234646330373138,Mega Drive,a:b23,b:b22,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android, +37333564393261653735306132613061,Mega Drive,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +64363363336633363736393038313464,Mega Drive,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b9,x:b2,y:b3,platform:Android, +33323763323132376537376266393366,Microsoft Dual Strike,a:b24,b:b23,back:b25,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,platform:Android, +30306461613834333439303734316539,Microsoft SideWinder Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b20,lefttrigger:b9,rightshoulder:b19,righttrigger:b10,start:b17,x:b2,y:b3,platform:Android, +32386235353630393033393135613831,Microsoft 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, +4d4f42415041442050726f2d48440000,Mobapad Chitu HD,a:b0,b:b1,back:b15,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, +4d4f435554452d303533582d4d35312d,Mocute 053X,a:b0,b:b1,back:b15,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, +33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,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, +39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,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, +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, +31316661666466633938376335383661,Nintendo Switch Pro Controller,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,start:b6,x:b3,y:b2,platform:Android, +34323437396534643531326161633738,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +50726f20436f6e74726f6c6c65720000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b2,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b10,rightx:a2,righty:a3,start:b18,y:b3,platform:Android, +36326533353166323965623661303933,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, +4e363420436f6e74726f6c6c65720000,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, +534e455320436f6e74726f6c6c657200,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +64623863346133633561626136366634,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +050000005509000003720000cf7f3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005509000010720000ffff3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005509000014720000df7f3f00,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, +050000005509000014720000df7f3f80,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,platform:Android, +37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown: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, +4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39383335313438623439373538343266,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b16,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,x:b1,y:b19,platform:Android, +4f5559412047616d6520436f6e74726f,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b6,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b19,platform:Android, +506572666f726d616e63652044657369,PDP PS3 Rock Candy Controller,a:b1,b:b17,back:h0.2,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, +61653962353232366130326530363061,Pokken,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a3,righty:a2,start:b30,x:b24,y:b21,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, +536f6e7920504c415953544154494f4e,PS3 Controller,a:b0,b:b1,back:b15,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, +61363034663839376638653463633865,PS3 Controller,a:b0,b:b1,back:b15,dpdown:b12,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 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, +66383132326164626636313737373037,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000004c050000c405000000783f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +050000004c050000c4050000fffe3f80,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a3,rightx:a4,righty:a5,start:b16,x:b0,y:b2,platform:Android, +050000004c050000c4050000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000004c050000cc090000fffe3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +30303839663330346632363232623138,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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 Controller,a:b1,b:b16,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +31373231336561636235613666323035,PS4 Controller,a:b0,b:b1,back:b15,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, +31663838336334393132303338353963,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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 Controller,a:b0,b:b1,back:b15,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, +35643031303033326130316330353564,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, +37626233336235343937333961353732,PS4 Controller,a:b0,b:b1,back:b15,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, +37626464343430636562316661643863,PS4 Controller,a:b0,b:b1,back:b15,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, +38393161636261653636653532386639,PS4 Controller,a:b0,b:b1,back:b15,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, +63313733393535663339656564343962,PS4 Controller,a:b0,b:b1,back:b15,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, +63393662363836383439353064663939,PS4 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, +65366465656364636137653363376531,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +66613532303965383534396638613230,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +050000004c050000e60c0000fffe3f80,PS5 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a3,rightx:a4,righty:a5,start:b16,x:b2,y:b17,platform:Android, +050000004c050000e60c0000ffff3f00,PS5 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +32346465346533616263386539323932,PS5 Controller,a:b0,b:b1,back:b15,dpdown: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, +32633532643734376632656664383733,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +37363764353731323963323639666565,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +64336263393933626535303339616332,Qanba 4RAF,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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: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, +05000000f8270000bf0b0000ffff3f00,Razer Kishi,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, +62653861643333663663383332396665,Razer Kishi,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +5a6869587520526574726f2042697420,Retro Bit Saturn Controller,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b26,rightshoulder:b27,righttrigger:b28,start:b30,x:b23,y:b24,platform:Android, +32417865732031314b6579732047616d,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +36313938306539326233393732613361,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +526574726f466c616720576972656420,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b2,y:b3,platform:Android, +61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,platform:Android, +526574726f696420506f636b65742043,Retroid Pocket,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +582d426f7820436f6e74726f6c6c6572,Retroid Pocket,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +64633735616665613536653363336132,Retroid Pocket,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b19,paddle2:b20,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,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:b12,dpleft:b13,dpright:b14,dpup:b11,start:b6,platform:Android, +37393234373533633333323633646531,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android, +5365616c6965436f6d707574696e6720,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android, +526574726f5553422e636f6d20534e45,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,platform:Android, +64643037633038386238303966376137,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,platform:Android, +37656564346533643138636436356230,Rock Candy Switch Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +33373336396634316434323337666361,RumblePad 2,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b24,platform:Android, +36363537303435333566386638366333,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +53616d73756e672047616d6520506164,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +66386565396238363534313863353065,Sanwa PlayOnline Mobile,a:b21,b:b22,back:b23,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b24,platform:Android, +32383165316333383766336338373261,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, +38613865396530353338373763623431,Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b10,rightshoulder:b20,righttrigger:b19,start:b17,x:b2,y:b3,platform:Android, +61316232336262373631343137633631,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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: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,x:b2,y:b3,platform:Android, +66633132393363353531373465633064,SG H510,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,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:b3,y:b2,platform:Android, +38376662666661636265313264613039,SNES,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, +5346432f555342205061640000000000,SNES Adapter,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, +5553422047616d657061642000000000,SNES Controller,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +62653335326261303663356263626339,Sony PlayStation Classic Controller,a:b19,b:b1,back:b17,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b3,rightshoulder:b10,righttrigger:b20,start:b18,x:b2,y:b0,platform:Android, +536f6e7920496e746572616374697665,Sony PlayStation Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +576972656c65737320436f6e74726f6c,Sony PlayStation Controller,a:b0,b:b1,back:b15,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, +63303964303462366136616266653561,Sony PSP,a:b21,b:b22,back:b27,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b23,y:b24,platform:Android, +63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,platform:Android, +35306436396437373135383665646464,SteelSeries Nimbus Plus,a:b0,b:b1,leftshoulder:b3,leftstick:b17,lefttrigger:b9,leftx:a0,rightshoulder:b20,rightstick:b18,righttrigger:b10,rightx:a2,x:b19,y:b2,platform:Android, +33313930373536613937326534303931,Taito Egret II Mini Control Panel,a:b25,b:b23,back:b27,guide:b30,leftx:a0,lefty:a1,rightshoulder:b21,righttrigger:b22,start:b28,x:b29,y:b24,platform:Android, +54475a20436f6e74726f6c6c65720000,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,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, +62363434353532386238336663643836,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,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, +37323236633763666465316365313236,THEC64 Joystick,a:b21,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b27,x:b23,y:b24,platform:Android, +38346162326232346533316164363336,THEGamepad,a:b23,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b24,y:b21,platform:Android, +050000004f0400000ed00000fffe3f00,Thrustmaster eSwap Pro Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +5477696e20555342204a6f7973746963,Twin Joystick,a:b22,b:b21,back:b28,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +31643365666432386133346639383937,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +30386438313564306161393537333663,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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 Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,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, +30396232393162346330326334636566,Xbox 360 Controller,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 Controller,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, +58626f782033363020576972656c6573,Xbox 360 Controller,a:b0,b:b1,back:b15,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, +65353331386662343338643939643636,Xbox 360 Controller,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 Controller,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, +47656e6572696320582d426f78207061,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +4d6963726f736f667420582d426f7820,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +64633436313965656664373634323364,Xbox Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e04000091020000ff073f00,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +050000005e04000091020000ff073f80,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000e00200000ffe3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, +050000005e040000e00200000ffe3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,platform:Android, +050000005e040000e0020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, +050000005e040000e0020000ffff3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,platform:Android, +050000005e040000fd020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +33356661323266333733373865656366,Xbox One Controller,a:b0,b:b1,back:b4,dpdown: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, +34356136633366613530316338376136,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +35623965373264386238353433656138,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +36616131643361333337396261666433,Xbox One Controller,a:b0,b:b1,back:b15,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, +58626f7820576972656c65737320436f,Xbox One Controller,a:b0,b:b1,back:b15,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, +65316262316265373335666131623538,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000000b000000783f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +050000005e040000000b000000783f80,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000050b0000ffff3f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000e002000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000ea02000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000fd020000ff7f3f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000120b000000783f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +050000005e040000120b000000783f80,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown: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, +65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,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, +050000001727000044310000ffff3f00,XiaoMi Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, + +# iOS +05000000ac0500000100000000006d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, +05000000ac050000010000004f066d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, +05000000ac05000001000000cf076d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, +05000000ac05000001000000df076d01,*,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, +05000000ac05000001000000ff076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, +05000000ac0500000200000000006d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS, +05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS, +05000000ac05000004000000a8986d04,8BitDo Micro,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,lefttrigger:b12,rightshoulder:b13,righttrigger:b14,start:b3,x:b6,y:b5,platform:iOS, +05000000ac05000004000000fd216d04,8BitDo Pro 2,a:b3,b:b2,back:b6,dpdown:b9,dpleft:b10,dpright:b11,dpup:b12,guide:b4,leftshoulder:b13,leftstick:b14,lefttrigger:+a2,leftx:a0,lefty:a1~,paddle1:b1,paddle2:b0,rightshoulder:b16,rightstick:b17,righttrigger:+a5,rightx:a3,righty:a4~,start:b5,x:b8,y:b7,platform:iOS, +05000000ac05000004000000209f6d04,8Bitdo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,leftstick:b12,lefttrigger:b13,leftx:a0,lefty:a1~,rightshoulder:b14,rightstick:b15,righttrigger:b16,rightx:a2,righty:a3~,start:b3,x:b6,y:b5,platform:iOS, +05000000ac050000040000003b8a6d04,8BitDo SN30 Pro+,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,leftstick:b12,lefttrigger:b13,leftx:a0,lefty:a1~,rightshoulder:b14,rightstick:b15,righttrigger:b16,rightx:a2,righty:a3~,start:b3,x:b6,y:b5,platform:iOS, +050000008a35000003010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, +050000008a35000004010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, +4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS, +4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS, +050000007e050000062000000f060000,Nintendo Switch Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,platform:iOS, +050000007e050000062000004f060000,Nintendo Switch Joy-Con (L),+leftx:h0.1,+lefty:h0.2,-leftx:h0.4,-lefty:h0.8,dpdown:b2,dpleft:b0,dpright:b3,dpup:b1,leftshoulder:b4,misc1:b6,rightshoulder:b5,platform:iOS, +050000007e05000008200000df070000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,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:b3,y:b2,platform:iOS, +050000007e0500000e200000df070000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,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:b3,y:b2,platform:iOS, +050000007e050000072000000f060000,Nintendo Switch Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,platform:iOS, +050000007e050000072000004f060000,Nintendo Switch Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,platform:iOS, +050000007e05000009200000df870000,Nintendo Switch Pro Controller,a:b1,b:b0,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:b3,y:b2,platform:iOS, +050000007e05000009200000ff870000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b3,y:b2,platform:iOS, +050000004c050000cc090000df070000,PS4 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, +050000004c050000cc090000df870001,PS4 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, +050000004c050000cc090000ff070000,PS4 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, +050000004c050000cc090000ff870001,PS4 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,touchpad:b11,x:b2,y:b3,platform:iOS, +050000004c050000cc090000ff876d01,PS4 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, +050000004c050000e60c0000df870000,PS5 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,touchpad:b10,x:b2,y:b3,platform:iOS, +050000004c050000e60c0000ff870000,PS5 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,touchpad:b11,x:b2,y:b3,platform:iOS, +05000000ac0500000300000000006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS, +05000000ac0500000300000043006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS, +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:iOS, +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/mac_controller_uart_bridge.py b/mac_controller_uart_bridge.py index 7cad9dd..0d0547c 100644 --- a/mac_controller_uart_bridge.py +++ b/mac_controller_uart_bridge.py @@ -20,6 +20,7 @@ import struct import threading import time from dataclasses import dataclass, field +from pathlib import Path from typing import Dict, List, Optional, Tuple import serial @@ -362,6 +363,12 @@ def build_arg_parser() -> argparse.ArgumentParser: default=UART_BAUD, help=f"UART baud rate (default {UART_BAUD}; must match switch-pico firmware)", ) + parser.add_argument( + "--sdl-mapping", + action="append", + default=[], + help="Path to an SDL2 controller mapping database (e.g. controllerdb.txt). Repeatable.", + ) return parser @@ -372,7 +379,24 @@ def main() -> None: deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) - sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER) + # Load bundled mapping plus any user-supplied mapping files. + default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" + mappings_to_load = [] + if default_mapping.exists(): + mappings_to_load.append(str(default_mapping)) + mappings_to_load.extend(args.sdl_mapping) + for mapping_path in mappings_to_load: + try: + loaded = sdl2.SDL_GameControllerAddMappingsFromFile(mapping_path.encode()) + console = Console() + console.print(f"[green]Loaded {loaded} SDL mapping(s) from {mapping_path}[/green]") + except Exception as exc: + console = Console() + console.print(f"[red]Failed to load SDL mapping {mapping_path}: {exc}[/red]") + + sdl2.SDL_SetHint(sdl2.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, b"1") + if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_JOYSTICK) != 0: + parser.error(f"SDL init failed: {sdl2.SDL_GetError().decode(errors='ignore')}") contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] mapping_by_index: Dict[int, str] = {} @@ -390,6 +414,10 @@ def main() -> None: console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") controller_indices.append(index) controller_names[index] = name_str + else: + name = sdl2.SDL_JoystickNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print(f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]") mappings = list(args.map) if args.interactive: @@ -442,6 +470,11 @@ def main() -> None: # Open currently connected controllers that match the mapping. for index, port in mappings: if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): + # Try to turn non-GameController into a controller via hint reload. + if index < sdl2.SDL_NumJoysticks(): + name = sdl2.SDL_JoystickNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print(f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") continue try: controller, instance_id = open_controller(index) From fc917afcd34e5a8b852b396abb1eff9ce50dd7f1 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 21 Nov 2025 08:11:24 -0700 Subject: [PATCH 018/134] Make other controllers work --- controller_db/gamecontrollerdb.txt | 2197 ++++++++++++++++++++++++++++ mac_controller_uart_bridge.py | 35 +- 2 files changed, 2231 insertions(+), 1 deletion(-) create mode 100644 controller_db/gamecontrollerdb.txt diff --git a/controller_db/gamecontrollerdb.txt b/controller_db/gamecontrollerdb.txt new file mode 100644 index 0000000..a288a13 --- /dev/null +++ b/controller_db/gamecontrollerdb.txt @@ -0,0 +1,2197 @@ +# Game Controller DB for SDL in 2.0.16 format +# Source: https://github.com/mdqinc/SDL_GameControllerDB + +# Windows +03000000300f00000a01000000000000,3 In 1 Conversion 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:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, +03000000fa190000918d000000000000,3 In 1 Conversion 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:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, +03000000fa2d00000100000000000000,3dRudder Foot Motion Controller,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows, +03000000d0160000040d000000000000,4Play Adapter,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 Adapter,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 Adapter,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 Adapter,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 Adapter,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, +03000000c82d00001930000000000000,8BitDo 64,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,righttrigger:b9,rightx:a3,righty:a4,start:b11,platform:Windows, +03000000c82d00000031000000000000,8BitDo Adapter,a:b0,b:b1,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:b3,y:b4,platform:Windows, +03000000c82d00000531000000000000,8BitDo Adapter 2,a:b0,b:b1,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:b3,y:b4,platform:Windows, +03000000c82d00000951000000000000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows, +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, +03000000c82d00006a28000000000000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Windows, +03000000c82d00001251000000000000,8BitDo Lite 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, +03000000c82d00001151000000000000,8BitDo Lite SE,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, +03000000c82d00000150000000000000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000151000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00005106000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,guide:b2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00002090000000000000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00000451000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows, +03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +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, +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, +03000000c82d00006928000000000000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,platform:Windows, +03000000c82d00002590000000000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +030000003512000012ab000000000000,8BitDo NES30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Windows, +03000000c82d000012ab000000000000,8BitDo NES30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000022000000090000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +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:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00000851000000000000,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:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,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, +03000000c82d00000361000000000000,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, +03000000c82d00000660000000000000,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, +03000000c82d00000960000000000000,8BitDo Pro 3,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,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000131000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000231000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000331000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000431000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00002867000000000000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b10,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, +03000000c82d00000060000000000000,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:Windows, +03000000c82d00000061000000000000,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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000102800000900000000000000,8BitDo SFC30,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, +03000000c82d000021ab000000000000,8BitDo SFC30,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, +03000000c82d00003028000000000000,8BitDo SFC30,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, +030000003512000020ab000000000000,8BitDo SN30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000030000000000000,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,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a2,rightshoulder:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00001290000000000000,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, +03000000c82d000020ab000000000000,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, +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, +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, +03000000c82d00000260000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000261000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001260000000000000,8BitDo Ultimate 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001b30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001d30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001730000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001130000000000000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001330000000000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00000121000000000000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000a00500003232000000000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, +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, +03000000d1180000402c000000000000,ADT1,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a3,rightx:a2,righty:a5,x:b3,y:b4,platform:Windows, +030000006f0e00008801000000000000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00001401000000000000,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, +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, +030000006f0e00001301000000000000,Afterglow 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, +030000006f0e00001302000000000000,Afterglow 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, +030000006f0e00001304000000000000,Afterglow 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, +030000006f0e00001413000000000000,Afterglow Xbox 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, +030000006f0e00003901000000000000,Afterglow 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, +03000000ab1200000103000000000000,Afterglow 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:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b000000f9000000000000,Afterglow 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, +03000000100000008200000000000000,Akishop Customs PS360,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, +030000007c1800000006000000000000,Alienware Dual Compatible PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,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, +0300000008100000e501000000000000,Anbernic Game Pad,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000020500000913000000000000,Anbernic RG P01,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:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000373500000710000000000000,Anbernic RG P01,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, +03000000373500004610000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000190e00000110000000000000,Aquaplus Piece,a:b1,b:b0,back:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start: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, +03000000490b00004406000000000000,ASCII Seamic Controller,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, +03000000869800002500000000000000,Astro C40 TR 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, +03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-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, +03000000050b00000579000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000050b00000679000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,start:b3,platform:Windows, +03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, +03000000380800001889000000000000,AtGames Legends Gamer Pro,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b14,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000008a3500000102000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, +030000008a3500000201000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, +030000008a3500000302000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, +030000008a3500000402000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, +03000000e4150000103f000000000000,Batarang,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000d6200000e557000000000000,Batarang PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, +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,touchpad:b13,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,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, +03000000790000000700000000000000,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:a4,start:b9,x:b3,y:b0,platform:Windows, +03000000808300000300000000000000,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:a4,start:b9,x:b3,y:b0,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, +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, +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, +03000000120c0000200e000000000000,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,touchpad:b13,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,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000120c0000f10e000000000000,Brook PS2 Adapter,a:b1,b:b2,back:b13,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, +03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,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, +030000005a1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows, +030000005b1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows, +030000005b1c00002500000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,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, +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,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 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,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, +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, +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 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, +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, +03000000bc2000000091000000000000,EasySMX 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, +030000006e0500000a20000000000000,Elecom DUX60 MMO,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, +03000095090000010000000000000000,Elecom JC-U609,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows, +0300004112000000e500000000000000,Elecom JC-U909Z,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows, +03000041120000001050000000000000,Elecom JC-U911,a:b1,b:b2,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b0,x:b4,y:b5,platform:Windows, +030000006e0500000520000000000000,Elecom P301U PlayStation Controller Adapter,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, +03000000250900000218000000000000,Elecom PlayStation 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: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,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, +030000007d0400000640000000000000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,platform: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, +03000000062000001801000000000000,EMS TrioLinker Plus II,a:b0,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:b2,y:b3,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 Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000006f0e00008401000000000000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00008101000000000000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00008001000000000000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000021000000090000000000000,FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +0300000011040000c600000000000000,FC801,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, +03000000852100000201000000000000,FF GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000ad1b000028f0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b00002ef0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000ad1b000038f0000000000000,Fightpad TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, +03005036852100000000000000000000,Final Fantasy XIV Online Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000f806000001a3000000000000,Firestorm,a:b9,b:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b10,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b12,x:b8,y:b4,platform:Windows, +03000000b50700000399000000000000,Firestorm 2,a:b2,b:b4,back:b10,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,righttrigger:b9,start:b11,x:b3,y:b5,platform:Windows, +03000000b50700001302000000000000,Firestorm D3,a:b0,b:b2,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,x:b1,y:b3,platform:Windows, +03000000b40400001024000000000000,Flydigi Apex,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000151900004000000000000000,Flydigi Vader 2,a:b27,b:b26,back:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b23,leftstick:b17,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b22,rightstick:b16,righttrigger:b20,rightx:a3,righty:a4,start:b18,x:b25,y:b24,platform:Windows, +03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b14,paddle1:b4,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Windows, +03000000b40400001224000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b5,x:b2,y:b3,platform:Windows, +0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, +03000000260900002625000000000000,GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows, +03000000341a000005f7000000000000,GameCube Controller,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 Controller,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 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:Windows, +03000000790000004618000000000000,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, +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, +03000000ac0500003d03000000000000,GameSir G3,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, +03000000ac0500005b05000000000000,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, +03000000ac0500002d02000000000000,GameSir G4,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, +03000000ac0500004d04000000000000,GameSir G4,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, +03000000ac0500001a06000000000000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000373500009410000000000000,GameSir Tegenaria Lite,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, +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 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, +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,touchpad:b13,x:b0,y:b3,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,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, +030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, +03000000300f00000b01000000000000,GGE909 Recoil,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, +03000000f0250000c283000000000000,Gioteck PlayStation 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, +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, +03000000f025000031c1000000000000,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 PlayStation 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 PlayStation 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, +03000000d11800000094000000000000,Google 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, +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, +030000007d0400000840000000000000,Gravis Destroyer Tilt,+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, +030000007d0400000540000000000000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000280400000140000000000000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a3,dpup:-a4,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,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, +030000008a2e0000dd10000000000000,Hand Held Legend GC Ultimate,a:b0,b:b2,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,misc2:b24,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b1,y:b3,platform:Windows, +030000008a2e0000df10000000000000,Hand Held Legend ProGCC,a:b1,b:b0,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b3,y:b2,platform:Windows, +030000000d0f00004900000000000000,Hatsune Miku Sho 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, +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, +030000000d0f00008500000000000000,Hori 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, +030000000d0f00002500000000000000,Hori Fighting Commander 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: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,touchpad:b13,x:b0,y:b3,platform:Windows, +030000000d0f00008400000000000000,Hori 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, +030000000d0f00006201000000000000,Hori Fighting Commander Octa,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,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006401000000000000,Hori Fighting Commander Octa,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,start:b7,x:b2,y:b3,platform:Windows, +030000000d0f00005100000000000000,Hori Fighting Commander PS3,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, +030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,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 Xbox 360,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, +030000000d0f00008800000000000000,Hori Fighting Stick mini 4 PS3,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, +030000000d0f00008700000000000000,Hori Fighting Stick mini 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:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000000d0f00001000000000000000,Hori Fightstick,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, +030000000d0f00003200000000000000,Hori Fightstick 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 Fightstick 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 Fightstick 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 Fightstick 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 Fightstick 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 Fightstick 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, +030000000d0f00002700000000000000,Hori Fightstick 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, +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, +030000000d0f0000a500000000000000,Hori Miku Project Diva X HD 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:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000000d0f0000a600000000000000,Hori Miku Project Diva X HD 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,touchpad: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,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, +030000000d0f00002301000000000000,Hori 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, +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,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Windows, +030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000c900000000000000,Hori Taiko Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +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, +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,touchpad:b13,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, +030000000d0f0000ee00000000000000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000c100000000000000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f0000f600000000000000,Horipad Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000000d0f00000202000000000000,Horipad O Nintendo Switch 2 Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,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, +030000000d0f00009601000000000000,Horipad Steam,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,misc2:b2,paddle1:b5,paddle2:b15,paddle3:b18,paddle4:b19,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +030000000d0f0000dc00000000000000,Horipad Switch,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000242e00000b20000000000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Windows, +03000000242e0000ff0b000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Windows, +03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows, +03000000242e00006a48000000000000,Hyperkin RetroN Sq,a:b3,b:b7,back:b5,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b0,rightshoulder:b1,start:b4,x:b2,y:b6,platform:Windows, +03000000242f00000a20000000000000,Hyperkin Scout,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, +03000000242e00000a20000000000000,Hyperkin Scout Premium SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, +03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Windows, +03000000f00300008d04000000000000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:-a2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:+a5,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows, +03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000d81d00001000000000000000,iBuffalo BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +030000005c0a00000285000000000000,iDroidCon,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b6,platform:Windows, +03000000696400006964000000000000,iDroidCon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000511d00000230000000000000,iGUGU Gamecore,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b1,leftstick:b4,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b2,platform:Windows, +03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, +030000006f0e00002401000000000000,Injustice Fightstick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +03000000ef0500000300000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, +03000000fd0500000230000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows, +03000000fd0500000030000000000000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Windows, +03000000fd0500003902000000000000,InterAct 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,InterAct 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,InterAct 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, +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,+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, +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, +03000000250900000017000000000000,Joypad 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, +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, +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 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 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,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000006d04000018c2000000000000,Logitech F510,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, +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, +030000006d04000019c2000000000000,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: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, +030000006d0400001ac2000000000000,Logitech Precision,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, +030000006d0400000bc2000000000000,Logitech WingMan 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, +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, +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,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000380700008031000000000000,Mad Catz FightStick Alpha PS3,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, +030000003807000038b7000000000000,Mad Catz 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, +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,touchpad:b13,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,touchpad:b13,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, +03000000380700008232000000000000,Mad Catz PlayStation 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, +03000000380700008731000000000000,Mad Catz PlayStation 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, +030000003807000056a8000000000000,Mad Catz 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, +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,touchpad:b13,x:b0,y:b3,platform:Windows, +03000000380700001847000000000000,Mad Catz Street Fighter 4 Xbox 360 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, +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,touchpad:b13,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, +030000008f0e00001330000000000000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000242f00003700000000000000,Mayflash F101,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000790000003018000000000000,Mayflash F300 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000242f00003900000000000000,Mayflash F300 Elite Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, +0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000242f0000f500000000000000,Mayflash N64 Adapter,a:b2,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows, +03000000242f0000f400000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows, +03000000790000007918000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,righttrigger:b7,rightx:a3,righty:a2,start:b8,platform:Windows, +030000008f0e00001030000000000000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, +03000000790000000318000000000000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, +03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000790000002418000000000000,Mega Drive Controller,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 Controller,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 Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,righttrigger:b2,start:b3,platform:Windows, +030000005e0400002800000000000000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform: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, +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 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, +030000004523000015e0000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000491900000904000000000000,Mobapad Chitu HD,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,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000ffff00000000000000000000,Mocute M053,a:b3,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b11,leftstick:b7,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b6,righttrigger:b4,rightx:a3,righty:a4,start:b8,x:b1,y:b0,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, +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, +03000000091200004488000000000000,MUSIA PlayStation 2 Input Display,a:b0,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:b11,rightx:a2,righty:a3,start:b5,x:b1,y:b3,platform:Windows, +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,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +0300000085320000170d000000000000,Nacon Revolution 5 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,touchpad:b13,x:b0,y:b3,platform:Windows, +0300000085320000190d000000000000,Nacon Revolution 5 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,touchpad:b13,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,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,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 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, +0300000092120000474e000000000000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows, +03000000921200004346000000000000,NES Controller,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-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, +03000000ec110000e1a7000000000000,Nintendo Switch,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, +030000007e0500006920000000000000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,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,misc1:b13,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, +030000007e0500007320000000000000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Windows, +030000007e0500001920000000000000,NSO 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,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Windows, +030000007e0500001720000000000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Windows, +03000000550900001472000000000000,NVIDIA Controller,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, +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,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 EX,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:a3,righty:a2,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, +03000000791d00000900000000000000,Nyko Playpad,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, +03000000782300000a10000000000000,Onlive 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, +0300000009120000072f000000000000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:-a2,leftx:a0,lefty:a1,righttrigger:-a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows, +03000000120c0000f60e000000000000,P4 Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, +03000000790000002201000000000000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +030000006f0e00008501000000000000,PDP Fightpad Pro GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000006f0e00000901000000000000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00008901000000000000,PDP Realmz Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000008f0e00004100000000000000,PlaySega,a:b1,b:b0,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b4,y:b3,platform:Windows, +03000000d620000011a7000000000000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000dd62000015a7000000000000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d620000012a7000000000000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d620000013a7000000000000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d62000003340000000000000,PowerA OPS Pro 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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000d62000002640000000000000,PowerA OPS 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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +0300000062060000d570000000000000,PowerA PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d620000014a7000000000000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006d04000084ca000000000000,Precision,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, +03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000c62400001a53000000000000,Pro Ex Mini,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +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, +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, +030000008f0e00007530000000000000,PS1 Controller,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:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000100800000300000000000000,PS2 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, +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, +03000000250900008888000000000000,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, +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, +03000000151a00006222000000000000,PS2 Dual Plus 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, +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, +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.4,dpleft:h0.8,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:b3,y:b0,platform:Windows, +03000000888800000804000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform: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, +03000000120c00000807000000000000,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, +03000000120c0000111e000000000000,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, +03000000120c0000121e000000000000,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, +03000000120c0000130e000000000000,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, +03000000120c0000150e000000000000,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, +03000000120c0000180e000000000000,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, +03000000120c0000181e000000000000,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, +03000000120c0000191e000000000000,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, +03000000120c00001e0e000000000000,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, +03000000120c0000a957000000000000,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, +03000000120c0000aa57000000000000,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, +03000000120c0000f21c000000000000,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, +03000000120c0000f31c000000000000,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, +03000000120c0000f41c000000000000,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, +03000000120c0000f51c000000000000,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, +03000000120c0000f70e000000000000,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, +03000000120e0000120c000000000000,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, +03000000160e0000120c000000000000,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, +030000001a1e0000120c000000000000,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, +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,touchpad:b13,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,touchpad:b13,x:b0,y:b3,platform:Windows, +030000004c0500005f0e000000000000,PS5 Access 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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000004c050000f20d000000000000,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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,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, +03000000222c00000025000000000000,Qanba Dragon Arcade Joystick,a: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, +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,touchpad:b13,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, +03000000ff1100004733000000000000,Ramox FPS 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,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows, +030000009b2800002300000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows, +030000009b2800006900000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows, +030000009b2800000800000000000000,Raphnet Dreamcast Adapter,a:b2,b:b1,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,lefttrigger:a2,leftx:a0,righttrigger:a3,righty:a1,start:b3,x:b10,y:b9,platform:Windows, +030000009b280000d000000000000000,Raphnet Dreamcast Adapter,a:b1,b:b0,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,lefttrigger:+a5,leftx:a0,lefty:a1,righttrigger:+a2,start:b3,x:b5,y:b4,platform:Windows, +030000009b2800006200000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, +030000009b2800003200000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, +030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, +030000009b2800001800000000000000,Raphnet Jaguar Adapter,a:b2,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b10,start:b3,x:b11,y:b12,platform:Windows, +030000009b2800003c00000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, +030000009b2800006100000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, +030000009b2800006300000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, +030000009b2800006400000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, +030000009b2800000200000000000000,Raphnet NES Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,platform:Windows, +030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,back:b5,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b9,rightx:a3,righty:a4,start:b4,x:b0,y:b3,platform:Windows, +030000009b2800004300000000000000,Raphnet Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, +030000009b2800000500000000000000,Raphnet Saturn Adapter 2.0,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, +030000009b2800000300000000000000,Raphnet SNES 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, +030000009b2800002600000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800002e00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800002f00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800005600000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800005700000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800001e00000000000000,Raphnet Vectrex 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 Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, +03000000790000008f18000000000000,Rapoo Gamepad,a:b2,b:b1,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:b3,y:b0,platform:Windows, +0300000032150000a602000000000000,Razer Huntsman V3 Pro,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b12,dpright:b13,dpup:b11,guide:b10,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, +03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000f8270000bf0b000000000000,Razer Kishi,a:b6,b:b7,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b18,leftshoulder:b12,leftstick:b19,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b13,rightstick:b20,righttrigger:b15,rightx:a3,righty:a4,start:b17,x:b9,y:b10,platform:Windows, +03000000321500000204000000000000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,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 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,touchpad:b13,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, +03000000921200004547000000000000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b6,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, +03000000632500007805000000000000,Retro Fighters 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, +0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, +03000000bd12000013d0000000000000,Retrolink Sega Saturn Classic Controller,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 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:Windows, +03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Windows, +0300000000f000000300000000000000,RetroUSB RetroPad,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, +0300000000f00000f100000000000000,RetroUSB Super RetroPort,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,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,a: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, +030000006f0e00008701000000000000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000830500007030000000000000,Rockfire Space Ranger,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b9,righttrigger:b8,start:b2,x:b3,y:b4,platform:Windows, +03000000050b0000e318000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, +03000000050b0000e518000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, +03000000050b00005819000000000000,ROG Chakram Core,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, +03000000050b0000181a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, +03000000050b00001a1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, +03000000050b00001c1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,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, +030000000d0f0000ad00000000000000,RX Gamepad,a:b0,b:b4,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b6,start:b9,x:b2,y:b1,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, +030000006f0e00001311000000000000,Saffun Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,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:b1,y:b0,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,a: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,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,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,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, +03000000a30600002106000000000000,Saitek PS1000 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000a306000020f6000000000000,Saitek PS2700 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00001101000000000000,Saitek Rumble,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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows, +030000009d0d00001130000000000000,Sanwa PlayStation Adapter,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, +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 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,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, +03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows, +03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, +03000000d804000086e6000000000000,Sega Multi Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a2,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, +0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, +0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows, +03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,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, +03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, +03000000120c00001c1e000000000000,SnakeByte 4S 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, +03000000140300000918000000000000,SNES Controller,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:Windows, +0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, +030000008f0e00000910000000000000,Sony 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, +03000000317300000100000000000000,Sony 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, +03000000666600006706000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows, +03000000e30500009605000000000000,Sony PlayStation 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:Windows, +03000000fe1400002a23000000000000,Sony PlayStation Adapter,a:b0,b:b1,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,x:b2,y:b3,platform:Windows, +030000004c050000da0c000000000000,Sony 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, +03000000632500002306000000000000,Sony PlayStation 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, +03000000f0250000c183000000000000,Sony PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000d9040000160f000000000000,Sony 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, +03000000ff000000cb01000000000000,Sony PlayStation Portable,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, +030000004c0500003713000000000000,Sony 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, +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, +03000000380700001722000000000000,Speedlink Competition Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,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, +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,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, +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, +03000000381000003014000000000000,SteelSeries Stratus Duo,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, +03000000381000003114000000000000,SteelSeries Stratus Duo,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, +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, +03000000380700003847000000000000,Street Fighter Fightstick 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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, +03000000790000000418000000000000,Super Famicom Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, +03000000341200001300000000000000,Super Racer,a:b2,b:b3,back:b8,leftshoulder:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b7,x:b0,y:b1,platform:Windows, +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, +03000000e40a00000307000000000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows, +03000000e40a00000207000000000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,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, +03000000790000001c18000000000000,TGZ 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:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000790000002601000000000000,TGZ 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, +03000000591c00002400000000000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, +03000000591c00002600000000000000,THEGamepad,a:b2,b:b1,back:b6,leftx:a0,lefty:a1,start:b7,x:b3,y:b0,platform:Windows, +030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, +030000004f04000023b3000000000000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004f0400000ed0000000000000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004f04000008d0000000000000,Thrustmaster Ferrari 150 PlayStation Controller,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, +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,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,Thrustmaster Run N Drive PlayStation Controller,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,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,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, +03000000666600000288000000000000,TigerGame PlayStation 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, +03000000666600000488000000000000,TigerGame PlayStation 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,a: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, +03000000571d00002100000000000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Windows, +03000000571d00002000000000000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,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 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, +03000000151900005678000000000000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,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, +030000000b0400003065000000000000,USB 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 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 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 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 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 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 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 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 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 Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, +03000000ff1100004133000000000000,USB 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, +03000000882800000305000000000000,V5 Game Pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,x:b2,y:b3,platform:Windows, +03000000790000001a18000000000000,Venom PS4 Arcade Joystick,a: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 PS4 Arcade Joystick,a: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, +030000006f0e00000302000000000000,Victrix PS4 Pro 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,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, +030000006f0e00000702000000000000,Victrix PS4 Pro Fightstick,a: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,touchpad:b13,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,Wii U 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, +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, +03000000380700002847000000000000,Xbox 360 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, +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, +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, +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, +030000005e040000ea02000000000000,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, +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, +030000005e040000ff02000000000000,Xbox One 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, +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 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, +03000000e0ff00000201000000000000,Xiaomi Black Shark (L),back:b0,dpdown:b11,dpleft:b9,dpright:b10,dpup:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,platform:Windows, +03000000172700004431000000000000,Xiaomi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, +03000000172700003350000000000000,Xiaomi XMGP01YM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,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,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows, +03000000c0160000e105000000000000,XinMo Dual Arcade,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, +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, +030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows, +030000002c3600000100000000000000,Yawman Arrow,+rightx:h0.2,+righty:h0.4,-rightx:h0.8,-righty:h0.1,a:b4,b:b5,back:b6,dpdown:b15,dpleft:b14,dpright:b16,dpup:b13,leftshoulder:b10,leftstick:b0,lefttrigger:-a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b12,rightshoulder:b8,rightstick:b9,righttrigger:+a4,start:b3,x:b1,y:b2,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, +03000000073500000400000000000000,Zenaim Arcade 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,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000120c00000500000000000000,Zeroplus Adapter,a:b2,b:b1,back:b11,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:b0,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, +03000000120c0000101e000000000000,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:Windows, + +# Mac OS X +030000008f0e00000300000009010000,2 In 1 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, +03000000c82d00001930000000020000,8BitDo 64,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,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X, +03000000c82d00001930000001000000,8BitDo 64,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,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X, +03000000c82d00000031000001000000,8BitDo Adapter,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:Mac OS X, +03000000c82d00000531000000020000,8BitDo Adapter 2,a:b0,b:b1,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:b3,y:b4,platform:Mac OS X, +03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X, +03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Mac OS X, +03000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001251000000020000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001151000000020000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000a30c00002400000006020000,8BitDo M30,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,guide:b9,leftshoulder:b6,lefttrigger:b5,rightshoulder:b4,righttrigger:b7,start:b8,x:b3,y:b0,platform:Mac OS X, +03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,lefttrigger:a5,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X, +03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X, +03000000c82d00002590000000010000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00002690000000010000,8BitDo NEOGEO,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b10,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +030000003512000012ab000001000000,8BitDo NES30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d000012ab000001000000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00002028000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +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, +03000000c82d00000751000000010000,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,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000660000000020000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000131000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000231000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000331000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000431000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00002867000000010000,8BitDo S30,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,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Mac OS X, +03000000c82d00003028000000010000,8Bitdo SFC30 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, +03000000102800000900000000000000,8BitDo SFC30 Joystick,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, +03000000c82d00000351000000010000,8BitDo SN30,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:Mac OS X, +03000000c82d00001290000001000000,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:Mac OS X, +03000000c82d00004028000000010000,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: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 Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001d30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001530000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001630000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001730000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001130000000020000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001330000000020000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001330000001000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000a00500003232000009010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X, +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, +0300000008100000e501000019040000,Anbernic Handheld,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:a4,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-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, +03000000050b00000579000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b42,paddle1:b9,paddle2:b11,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, +03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, +03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X, +03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X, +03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X, +030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, +030000008a3500000201000000010000,Backbone One,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, +030000008a3500000202000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, +030000008a3500000402000000010000,Backbone One,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, +030000008a3500000302000000010000,Backbone One PlayStation Edition,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,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,touchpad:b13,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,touchpad:b13,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,touchpad:b13,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 V3 Rumble Pad PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000791d00000103000009010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +030000006e0500000720000010020000,Elecom JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Mac OS X, +030000006f0e00008401000003010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000151900004000000001000000,Flydigi Vader 2,a:b14,b:b15,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Mac OS X, +03000000b40400001124000001040000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000b40400001224000003030000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X, +03000000ac0500001a06000002020000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000373500000411000023000000,GameSir X4A Xbox 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, +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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000006f0e00000102000000000000,GameStop 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, +03000000ff1100003133000007010000,GameWare PC Control Pad,a:b2,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:a3,righty:a4,start:b11,x:b3,y:b0,platform:Mac OS X, +03000000d11800000094000000010000,Google 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:Mac OS X, +030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform: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 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, +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,touchpad:b13,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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000000d0f00008400000000010000,Hori Fighting Commander 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: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,Hori Fighting Commander 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, +03000000341a00000302000014010000,Hori Fighting Stick Mini,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, +030000000d0f00008800000000010000,Hori Fighting Stick mini 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,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00008700000000010000,Hori Fighting Stick mini 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,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,touchpad:b13,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, +030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X, +030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +030000000d0f00000002000017010000,Hori Split Pad Fit,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,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, +030000000d0f0000c100000072050000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000242e0000ff0b000000010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Mac OS X, +03000000790000004e95000000010000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Mac OS X, +03000000830500006020000000000000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, +03000000ef0500000300000000020000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X, +03000000fd0500000030000010010000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Mac OS X, +030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X, +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 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, +030000006d04000019c2000000000000,Logitech Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006d04000016c2000000020000,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, +030000006d04000016c2000000030000,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, +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,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,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, +030000006d0400001fc2000000000000,Logitech F710,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,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, +03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a: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, +03000000380700005082000000010000,Mad Catz PS4 Fightpad 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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +03000000380700008483000000010000,Mad Catz PS4 Fightstick TE S Plus,a: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:Mac OS X, +0300000049190000020400001b010000,Manba One,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: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, +030000008f0e00001330000011010000,Mayflash Controller Adapter,a:b2,b:b4,back:b16,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b12,lefttrigger:b16,leftx:a0,lefty:a2,rightshoulder:b14,rightx:a6~,righty:a4,start:b18,x:b0,y:b6,platform:Mac OS X, +03000000790000004318000000010000,Mayflash GameCube Adapter,a:b4,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,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, +03000000d620000010a7000003010000,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:Mac OS X, +030000008f0e00001030000011010000,Mayflash Saturn Adapter,a:b0,b:b2,dpdown:b28,dpleft:b30,dpright:b26,dpup:b24,leftshoulder:b10,lefttrigger:b14,rightshoulder:b12,righttrigger:b4,start:b18,x:b6,y:b8,platform:Mac OS X, +0300000025090000e803000000000000,Mayflash Wii Classic Adapter,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, +03000000790000000318000000010000,Mayflash Wii DolphinBar,a:b8,b:b12,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b44,leftshoulder:b16,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b4,platform:Mac OS X, +03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, +03000000790000000018000000010000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, +030000005e0400002800000002010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Mac OS X, +030000005e0400000300000006010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Mac OS X, +030000005e0400000700000006010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Mac OS X, +030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Mac OS X, +030000004523000015e0000072050000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X, +03000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +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, +03000000853200008906000000010000,Nacon Revolution X Unlimited,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, +03000000ec110000e1a7000001010000,Nintendo Switch,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, +030000007e0500006920000001010000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,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, +030000007e0500000920000010020000,Nintendo Switch Pro 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:Mac OS X, +050000007e05000009200000ff070000,Nintendo Switch Pro 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:Mac OS X, +030000007e0500007320000001010000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Mac OS X, +030000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Mac OS X, +030000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000550900001472000025050000,NVIDIA Controller,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, +030000004b120000014d000000010000,Nyko Airflo EX,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:a3,righty:a2,start:b9,x:b2,y:b3,platform:Mac OS X, +0300000009120000072f000000010000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a2,leftx:a0,lefty:a1,righttrigger:a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Mac OS X, +030000006f0e00000901000002010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform: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, +03000000d620000011a7000000020000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000d620000011a7000010050000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, +030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, +030000004c0500006802000072050000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, +030000004c050000a00b000000010000,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:Mac OS X, +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:Mac OS X, +030000004c050000c405000000010000,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:Mac OS X, +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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +0300004b4c0500005f0e000000010000,PS5 Access 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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000e60c000000010000,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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000f20d000000010000,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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,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,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000005e040000e002000001000000,PXN P30 Pro Mobile,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:Mac OS X, +03000000222c00000225000000010000,Qanba Dragon Arcade Joystick 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, +03000000222c00000020000000010000,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,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, +030000009b2800005600000020020000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Mac OS X, +030000009b2800008000000022020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform: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 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, +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,touchpad:b13,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 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,touchpad:b13,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, +03000000632500008005000000010000,Redgear,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, +03000000632500002305000000010000,Redragon Saturn,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, +03000000921200004547000000020000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b2,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,lefttrigger:b14,rightshoulder:b10,righttrigger:b4,start:b12,x:b6,y:b8,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, +0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,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, +03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,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, +030000004c0500006802000002100000,Rii RK707,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b3,righttrigger:b9,rightx:a2,righty:a3,start:b1,x:b15,y:b12,platform:Mac OS X, +030000006f0e00008701000005010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000c6240000fefa000000000000,Rock Candy PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +03000000e804000000a000001b010000,Samsung EIGP20,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b11,leftx:a1,lefty:a3,rightshoulder:b12,rightx:a4,righty:a5,start:b16,x:b7,y:b9,platform:Mac OS X, +03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Mac OS X, +03000000a30c00002500000006020000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Mac OS X, +03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X, +03000000b40400000a01000000000000,Sega Saturn,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X, +030000003512000021ab000000000000,SFC30 Joystick,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, +0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X, +030000004c050000a00b000000000000,Sony DualShock 4 Adapter,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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000004c050000cc09000000000000,Sony DualShock 4 V2,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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +03000000666600006706000088020000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Mac OS X, +030000004c050000da0c000000010000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X, +030000004c0500003713000000010000,Sony 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, +030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +03000000110100002014000000000000,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, +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, +03000000381000003014000000000000,SteelSeries Stratus Duo,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, +03000000381000003114000000000000,SteelSeries Stratus Duo,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, +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, +030000000d0f0000f600000000010000,Switch Hori Pad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000457500002211000000010000,SZMY Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000e40a00000307000001000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X, +03000000e40a00000207000001000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X, +03000000790000001c18000000010000,TGZ 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: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, +03000000790000001c18000003100000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000591c00002400000021000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X, +03000000591c00002600000021000000,THEGamepad,a:b2,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Mac OS X, +030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X, +030000004f0400000ed0000000020000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X, +03000000571d00002100000021000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Mac OS X, +03000000bd12000015d0000000010000,Tomee Retro Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, +03000000bd12000015d0000000000000,Tomee 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:Mac OS X, +03000000571d00002000000021000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X, +030000005f140000c501000000020000,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:Mac OS X, +03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X, +03000000632500002605000000010000,Uberwith Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c0160000e105000000040000,Ultimate Atari Fight Stick,a:b2,b:b4,back:b18,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,rightshoulder:b8,righttrigger:b10,start:b16,x:b0,y:b6,platform:Mac OS X, +03000000151900005678000010010000,Uniplay U6,a:b3,b:b6,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,leftstick:b31,lefttrigger:b21,leftx:a1,lefty:a3,rightshoulder:b19,rightstick:b33,righttrigger:b23,rightx:a4,righty:a5,start:b27,x:b11,y:b13,platform:Mac OS X, +030000006f0e00000302000025040000,Victrix PS4 Pro 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,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, +030000006f0e00000702000003060000,Victrix PS4 Pro Fightstick,a: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,touchpad:b13,x:b0,y:b3,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,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, +030000005e0400008e02000010010000,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 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 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e040000050b000003090000,Xbox Elite Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000130b000011050000,Xbox One 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 One 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, +030000005e040000200b000013050000,Xbox One 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, +030000005e040000200b000015050000,Xbox One 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, +030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e040000dd02000000000000,Xbox One 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, +030000005e040000e002000000000000,Xbox One 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 One 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, +030000005e040000e302000000000000,Xbox One 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, +030000005e040000ea02000000000000,Xbox One 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 One 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, +030000005e040000220b000013050000,Xbox One Elite 2 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,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X, +03000000c62400003a54000000000000,Xbox One PowerA 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,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +030000005e040000130b000013050000,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, +030000005e040000130b000015050000,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, +030000005e040000130b000007050000,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, +030000005e040000130b000017050000,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, +030000005e040000130b000022050000,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, +030000005e040000220b000017050000,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, +03000000172700004431000029010000,XiaoMi 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,a: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,a: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 +03000000c82d00001930000011010000,8BitDo 64,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,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux, +05000000c82d00001930000001000000,8BitDo 64,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,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux, +03000000c82d00000031000011010000,8BitDo Adapter,a:b0,b:b1,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:b3,y:b4,platform:Linux, +03000000c82d00000631000000010000,8BitDo Adapter 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, +03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux, +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, +05000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Linux, +03000000c82d00001251000011010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00001151000011010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000a20000000020000,8BitDo M30 Xbox,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,start:b7,x:b2,y:b3,platform:Linux, +03000000c82d00002090000011010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux, +03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00006928000011010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux, +05000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux, +05000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000008000000210000011010000,8BitDo NES30,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux, +05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux, +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, +03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00001030000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000020000000000000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +06000000c82d00000020000006010000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c82d00000960000011010000,8BitDo Pro 3,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000231000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000331000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000431000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00002867000000010000,8BitDo S30,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,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Linux, +03000000c82d00000060000011010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +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, +030000003512000012ab000010010000,8BitDo SFC30,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, +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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +05000000102800000900000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00003028000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00000260000011010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000a31000014010000,8BitDo Ultimate 2C,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, +03000000c82d00001d30000011010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001130000011010000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000631000010010000,8BitDo Ultimate Wireless,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, +03000000c82d00000631000014010000,8BitDo Ultimate Wireless,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, +03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001330000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000c82d00000121000000010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, +05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00008801000011010000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00003901000013020000,Afterglow Prismatic Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00001302000000010000,Afterglow Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00003901000020060000,Afterglow 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, +03000000100000008200000011010000,Akishop Customs PS360,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 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux, +05000000491900000204000021000000,Amazon Fire 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, +05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,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:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, +0300000008100000e501000001010000,Anbernic Handheld,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Linux, +03000000020500000913000010010000,Anbernic RG P01,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, +03000000373500000710000010010000,Anbernic RG P01,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, +05000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000190e00000110000010010000,Aquaplus Piece,a:b1,b:b0,back:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b2,platform:Linux, +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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-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, +03000000050b00000579000011010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b36,paddle1:b52,paddle2:b53,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b21,paddle1:b22,paddle2:b23,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +03000000503200000110000011010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000044010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000046010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +03000000503200000210000000000000,Atari VCS Modern 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, +03000000503200000210000011010000,Atari VCS Modern 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, +05000000503200000210000000000000,Atari VCS Modern 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, +05000000503200000210000045010000,Atari VCS Modern 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, +05000000503200000210000046010000,Atari VCS Modern 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, +05000000503200000210000047010000,Atari VCS Modern 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, +030000008a3500000201000011010000,Backbone One,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, +030000008a3500000202000011010000,Backbone One,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, +030000008a3500000302000011010000,Backbone One,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, +030000008a3500000402000011010000,Backbone One,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,touchpad:b13,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 Controller 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, +03000000bc2000004d50000011010000,Beitong A1T2 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, +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, +03000000bc2000006412000011010000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b30,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, +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, +03000000120c0000300e000011010000,Brook Audio Fighting Board 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, +03000000120c0000310e000011010000,Brook Audio Fighting Board 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,touchpad:b13,x:b0,y:b3,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,touchpad:b13,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,touchpad:b13,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, +03000000d81d00000b00000010010000,Buffalo BSGP1601,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,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, +03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,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, +03000000632500007a05000001020000,Cosmic Byte Ares Wired 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: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 V3 Rumble,a: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, +030000005e0400008e02000002010000,Data Frog S80,a:b1,b:b0,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, +03000000791d00000103000010010000,Dual Box Wii Classic Adapter,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, +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, +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, +03000000242f00009100000000010000,EasySMX ESM-9101,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, +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, +030000006e0500000720000010010000,Elecom W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux, +030000007d0400000640000010010000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,platform:Linux, +03000000430b00000300000000010000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +030000006f0e00008401000011010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00008101000011010000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00008001000011010000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000852100000201000010010000,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:Linux, +05000000b40400001224000001010000,Flydigi APEX 4,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b20,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000b40400001124000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000b40400001224000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000007e0500003703000000000000,GameCube Adapter,a:b0,b:b1,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, +19000000030000000300000002030000,GameForce Controller,a:b1,b:b0,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, +03000000373500000b10000019010000,GameSir Cyclone 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, +03000000ac0500005b05000010010000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000558500001b06000010010000,GameSir G4 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000ac0500002d0200001b010000,GameSir G4s,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b33,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000ac0500007a05000011010000,GameSir G5,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000373500009710000001020000,GameSir Kaleid Flux,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000ac0500001a06000011010000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +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, +03000000373500009410000010010000,GameSir Tegenaria Lite,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, +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 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, +03000000f025000021c1000010010000,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:Linux, +03000000f0250000c283000010010000,Gioteck VX2 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, +190000004b4800000010000000010000,GO-Advance Controller,a:b1,b:b0,back:b10,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,leftshoulder:b4,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b13,start:b15,x:b2,y:b3,platform:Linux, +190000004b4800000010000001010000,GO-Advance Controller,a:b1,b:b0,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b13,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b16,righttrigger:b15,start:b17,x:b2,y:b3,platform:Linux, +190000004b4800000011000000010000,GO-Super Gamepad,a:b0,b:b1,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b3,y:b2,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, +03000000d11800000094000011010000,Google 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, +05000000d11800000094000000010000,Google 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, +0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000001010000,GPD Win Max 2 6800U Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000007d0400000540000000010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +03000000280400000140000000010000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000008f0e00000610000000010000,GreenAsia Electronics Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux, +030000008f0e00001200000010010000,GreenAsia 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, +030000008a2e0000dd10000011010000,Hand Held Legend GC Ultimate,a:b0,b:b2,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,misc2:b24,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b1,y:b3,platform:Linux, +030000008a2e0000df10000011010000,Hand Held Legend ProGCC,a:b1,b:b0,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b3,y:b2,platform:Linux, +06000000adde0000efbe000002010000,Hidromancer 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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, +03000000c9110000f055000011010000,HJC 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, +030000000d0f00006d00000020010000,Hori EDGE 301,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, +030000000d0f00008400000011010000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,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,touchpad:b13,x:b0,y:b3,platform:Linux, +030000000d0f00005001000009040000,Hori Fighting Commander Octa 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, +030000000d0f00008500000010010000,Hori Fighting Commander 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, +030000000d0f00008600000002010000,Hori Fighting Commander Xbox 360,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, +030000000d0f00003701000013010000,Hori Fighting Stick Mini,a:b1,b:b0,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:b3,y:b2,platform:Linux, +030000000d0f00008800000011010000,Hori Fighting Stick mini 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,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00008700000011010000,Hori Fighting Stick mini 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,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +030000000d0f00001000000011010000,Hori Fightstick 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, +03000000ad1b000003f5000033050000,Hori Fightstick VX,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b8,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, +030000000d0f00004d00000011010000,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: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,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, +030000000d0f00001100000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00002200000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006a00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, +030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000000d0f00008501000017010000,Hori Split Pad Fit,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, +030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,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: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, +030000000d0f0000c100000011010000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00006700000001010000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000000d0f0000ab01000011010000,Horipad Steam,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,misc2:b2,paddle1:b19,paddle2:b18,paddle3:b15,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000000d0f00009601000091000000,Horipad Steam,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,misc2:b2,paddle1:b19,paddle2:b18,paddle3:b15,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000000d0f0000f600000001000000,Horipad Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000341a000005f7000010010000,HuiJia GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, +05000000242e00000b20000001000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Linux, +03000000242e0000ff0b000011010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Linux, +03000000242e00006a38000010010000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Linux, +03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, +030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,platform:Linux, +050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, +03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux, +03000000120c00000500000010010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux, +03000000ef0500000300000000010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux, +03000000fd0500000030000000010000,InterAct GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Linux, +03000000fd0500002a26000000010000,InterAct HammerHead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux, +0500000049190000020400001b010000,Ipega PG 9069,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,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,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, +03000000300f00001101000010010000,Jess Tech Colour 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, +03000000300f00001001000010010000,Jess Tech Dual Analog Rumble,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,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 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, +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:Linux, +050000007e0500000620000001000000,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:Linux, +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:Linux, +050000007e0500000720000001000000,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:Linux, +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:Linux, +03000000242f00002d00000011010000,JYS 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 Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux, +030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d040000d1ca000000000000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d040000d1ca000011010000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d0400001dc2000014400000,Logitech F310,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, +030000006d0400001ec2000019200000,Logitech F510,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, +030000006d0400001ec2000020200000,Logitech F510,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,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,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, +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, +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, +03000000380700008532000010010000,Mad Catz Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,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,touchpad:b13,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, +03000000380700008031000011010000,Mad Catz FightStick Alpha 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, +03000000380700008081000011010000,Mad Catz 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,leftstick: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, +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,touchpad:b13,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,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000380700001888000010010000,Mad Catz 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,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000380700003888000010010000,Mad Catz Joystick,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, +03000000380700001647000010040000,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, +03000000380700003847000090040000,Mad Catz 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, +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, +030000008f0e00001330000010010000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Linux, +03000000790000004318000010010000,Mayflash GameCube 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, +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, +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, +030000008f0e00001030000010010000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux, +0300000025090000e803000001010000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, +03000000790000000318000011010000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux, +03000000790000000018000011010000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000b50700001203000010010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, +03000000b50700004f00000000010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux, +03000000780000000600000010010000,Microntek Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, +030000005e0400002800000000010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Linux, +030000005e0400000300000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux, +030000005e0400000700000000010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux, +030000005e0400000e00000000010000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, +030000005e0400002700000000010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Linux, +030000005e0400008502000000010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +030000005e0400008902000021010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +030000005e0400008e02000001000000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.1,dpleft:h0.2,dpright:h0.8,dpup:h0.4,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,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, +030000005e0400008e02000056210000,Microsoft 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, +030000005e0400008e02000062230000,Microsoft 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, +030000005e040000d102000001010000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000d102000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000dd02000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000ea02000008040000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000ea0200000f050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b000009050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000e302000003020000,Microsoft Xbox One Elite,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000000b000007040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000000b000008040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e0400008e02000030110000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000016050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000017050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b000001050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, +03000000790000001c18000010010000,Mobapad Chitu HD,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, +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, +05000000e80400006e0400001b010000,Mocute 053X M59,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, +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,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,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 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, +030000005e0400008e02000010020000,MSI GC20 V2,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,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, +03000000f70600000100000000010000,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:Linux, +030000006b1400000906000014010000,Nacon Asymmetric Wireless PS4 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, +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, +03000000853200000706000012010000,Nacon GC-100,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, +05000000853200000503000000010000,Nacon MG-X Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +0300000085320000170d000011010000,Nacon Revolution 5 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,touchpad:b13,x:b0,y:b3,platform:Linux, +0300000085320000190d000011010000,Nacon Revolution 5 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,touchpad:b13,x:b0,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, +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, +0300000092120000474e000000010000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,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, +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, +03000000ec110000e1a7000010010000,Nintendo Switch,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, +030000007e0500006920000011010000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Linux, +060000004e696e74656e646f20537700,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +060000007e0500000620000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +060000007e0500000820000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +050000004c69632050726f20436f6e00,Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b16,b:b15,back:b4,leftshoulder:b6,leftstick:b12,leftx:a1,lefty:a0~,rightshoulder:b8,start:b9,x:b14,y:b17,platform:Linux, +030000007e0500000920000000026803,Nintendo Switch Pro Controller,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Linux, +030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +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,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux, +05000000010000000100000003000000,Nintendo Wii Remote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +050000007e0500003003000001000000,Nintendo Wii U Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, +050000005a1d00000218000003000000,Nokia GC 5000,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, +030000000d0500000308000010010000,Nostromo n45 Dual Analog,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux, +030000007e0500007320000011010000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Linux, +030000007e0500001920000011810000,NSO N64 Controller,+rightx:b2,+righty:b3,-rightx:b4,-righty:b10,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b7,righttrigger:b9,start:b11,platform:Linux, +050000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Linux, +050000007e0500001920000001800000,NSO N64 Controller,+rightx:b2,+righty:b3,-rightx:b4,-righty:b10,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b7,righttrigger:b9,start:b11,platform:Linux, +030000007e0500001e20000011810000,NSO Sega Genesis Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,misc1:b3,rightshoulder:b2,righttrigger:b4,start:b5,platform:Linux, +030000007e0500001720000011810000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, +050000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b8,start:b10,x:b3,y:b2,platform:Linux, +050000007e0500001720000001800000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, +03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +03000000550900001472000011010000,NVIDIA Controller,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,misc1:b8,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, +05000000550900001472000001000000,NVIDIA Controller,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,misc1:b8,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, +030000004b120000014d000000010000,NYKO Airflo EX,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:a3,righty:a2,start:b9,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,ODROID Go 2,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,ODROID Go 2,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, +05000000362800000100000002010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, +05000000362800000100000003010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, +05000000362800000100000004010000,OUYA Controller,a:b0,b:b3,back:b14,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,start:b16,x:b1,y:b2,platform:Linux, +03000000830500005020000010010000,Padix Rockfire PlayStation 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, +03000000ff1100003133000010010000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000006f0e0000b802000001010000,PDP Afterglow 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 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, +030000006f0e0000d702000006640000,PDP Black Camo Wired Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b13,dpup:b14,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00008501000011010000,PDP Fightpad Pro Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00002801000011010000,PDP PS3 Rock Candy Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00000901000011010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00002f01000011010000,PDP Wired 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, +03000000ad1b000004f9000000010000,PDP Xbox 360 Versus Fighting,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e0000f102000000000000,PDP Xbox Atomic,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, +030000006f0e0000a802000023020000,PDP Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +030000006f0e0000a702000023020000,PDP Xbox One Raven Black,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e0000d802000006640000,PDP Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e0000ef02000007640000,PDP Xbox Series Kinetic Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +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, +03000000d62000000540000001010000,PowerA Advantage 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, +03000000d620000011a7000011010000,PowerA Core 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, +03000000dd62000015a7000011010000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000d620000012a7000011010000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000d62000000140000001010000,PowerA Fusion Pro 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000c62400001a53000000010000,PowerA Mini Pro Ex,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d620000013a7000011010000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000d620000014a7000011010000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000c62400001a58000001010000,PowerA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d62000000220000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, +03000000d62000000228000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c62400001a54000001010000,PowerA Xbox One Mini 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, +03000000d62000000240000001010000,PowerA Xbox One Spectra Infinity,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d62000000520000050010000,PowerA 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, +03000000d62000000b20000001010000,PowerA 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, +03000000d62000000f20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,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, +03000000250900000017000010010000,PS/SS/N64 Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b5,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2~,righty:a3,start:b8,platform:Linux, +03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, +03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,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, +030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,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, +030000005f1400003102000010010000,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:Linux, +030000006f0e00001402000011010000,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, +030000008f0e00000300000010010000,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:Linux, +050000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,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, +050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,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, +05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +030000004c050000a00b000011010000,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:Linux, +030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +030000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +030000004c050000c405000011010000,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:Linux, +030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +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,touchpad:b13,x:b0,y:b3,platform:Linux, +030000004c050000cc09000011010000,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:Linux, +030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +03000000c01100000140000011010000,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:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +0300004b4c0500005f0e000011010000,PS5 Access 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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +030000004c050000e60c000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +030000004c050000f20d000011010000,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:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +030000004c050000f20d000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +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,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +050000004c050000e60c000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +050000004c050000f20d000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +03000000300f00001211000011010000,Qanba Arcade Joystick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux, +03000000222c00000225000011010000,Qanba Dragon Arcade Joystick 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, +03000000222c00000025000011010000,Qanba Dragon Arcade Joystick 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,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000222c00001220000011010000,Qanba Drone 2 Arcade Joystick 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:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000222c00001020000011010000,Qanba Drone 2 Arcade Joystick PS5,a: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, +03000000222c00000020000011010000,Qanba Drone Arcade PS4 Joystick,a: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,rightshoulder:b5,righttrigger:a4,start:b9,touchpad:b13,x:b0,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, +03000000222c00000223000011010000,Qanba Obsidian Arcade Joystick 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, +03000000222c00000023000011010000,Qanba Obsidian Arcade Joystick 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,touchpad:b13,x:b0,y:b3,platform:Linux, +030000009b2800000300000001010000,Raphnet 4nes4snes,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, +030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux, +0300132d9b2800006500000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +0300132d9b2800006500000001010000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +030000009b2800006000000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +030000009b2800003c00000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux, +030000009b2800006100000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux, +030000009b2800006300000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux, +030000009b2800006400000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux, +030000009b2800008000000020020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux, +030000009b2800008000000001010000,Raphnet Wii Classic Adapter V3,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux, +03000000f8270000bf0b000011010000,Razer Kishi,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000321500000204000011010000,Razer Panthera PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000104000011010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000321500000810000011010000,Razer Panthera PS4 Evo Arcade Stick,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,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000321500000010000011010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +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: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 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,touchpad:b13,x:b0,y:b3,platform:Linux, +030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000321500000b10000011010000,Razer Wolverine PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +0300000032150000140a000001010000,Razer Wolverine Ultimate Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000000d0f0000c100000010010000,Retro Bit Legacy16,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b12,leftshoulder:b4,lefttrigger:b6,misc1:b13,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Linux, +0300000003040000c197000011010000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, +190000004b4800000111000000010000,RetroGame Joypad,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux, +0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, +00000000526574726f53746f6e653200,RetroStone 2 Controller,a:b1,b:b0,back:b10,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Linux, +03000000341200000400000000010000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start: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, +030000006f0e00008701000011010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +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, +030000006f0e00001311000011010000,Saffun Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,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:b1,y:b0,platform:Linux, +03000000a306000023f6000011010000,Saitek Cyborg PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, +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,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, +03000000a30600000d5f000010010000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux, +03000000a30600000c04000011010000,Saitek P2900,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,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,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, +03000000a306000020f6000011010000,Saitek PS2700 Rumble,a: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, +05000000e804000000a000001b010000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux, +03000000952e00004b43000011010000,Scuf Envision,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:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, +03000000952e00004d43000011010000,Scuf Envision,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:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, +03000000952e00004e43000011010000,Scuf Envision,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:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,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, +03000000790000001100000011010000,Sega Saturn,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,Sega Saturn,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,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux, +03000000632500002305000010010000,ShanWan 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, +03000000632500002605000010010000,ShanWan 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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000632500007505000010010000,ShanWan 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 Gamepad,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, +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, +030000004b2900000430000011000000,Snakebyte 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, +050000004c050000cc09000001000000,Sony DualShock 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,touchpad:b13,x:b0,y:b3,platform:Linux, +03000000666600006706000000010000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux, +030000004c050000da0c000011010000,Sony 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,Sony 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, +03000000ff000000cb01000010010000,Sony PlayStation Portable,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, +030000004c0500003713000011010000,Sony 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, +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,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,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, +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, +03000000de2800000112000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:+a5,dpleft:-a4,dpright:+a4,dpup:-a5,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,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, +03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, +03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,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, +03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, +03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,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, +05000000de2800000511000001000000,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, +05000000de2800000611000001000000,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, +03000000de2800000512000010010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux, +03000000de2800000512000011010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux, +03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux, +03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000ad1b000038f0000090040000,Street Fighter IV Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000003b07000004a1000000010000,Suncom SFX Plus,a:b0,b:b2,back:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux, +030000001f08000001e4000010010000,Super Famicom Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform: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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,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, +03000000457500000401000011010000,SZMY Power DS4 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: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, +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,a: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, +03000000e40a00000307000011010000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux, +03000000e40a00000207000011010000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux, +03000000ba2200000701000001010000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Linux, +03000000790000001c18000011010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000591c00002400000010010000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux, +03000000591c00002600000010010000,THEGamepad,a:b2,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux, +030000004f04000015b3000001010000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, +030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, +030000004f04000020b3000010010000,Thrustmaster Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, +030000004f04000023b3000000010000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform: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, +030000004f04000004b3000010010000,Thrustmaster Firestorm Dual Power,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, +030000004f04000026b3000002040000,Thrustmaster GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c6240000025b000002020000,Thrustmaster GPX,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, +030000004f04000008d0000000010000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004f04000009d0000000010000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000004f04000007d0000000010000,Thrustmaster T Mini,a: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, +03000000571d00002000000010010000,Tomee SNES Adapter,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, +03000000bd12000015d0000010010000,Tomee 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, +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, +03000000680a00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux, +03000000780300000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux, +03000000e00d00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux, +03000000f00600000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux, +030000005f140000c501000010010000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +06000000f51000000870000003010000,Turtle Beach Recon,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000100800000100000010010000,Twin PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +03000000c0160000e105000010010000,Ultimate Atari Fight Stick,a:b1,b:b2,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b8,x:b0,y:b3,platform:Linux, +03000000151900005678000010010000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +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 Gamepad,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux, +03000000790000001a18000011010000,Venom PS4 Arcade Joystick,a: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, +03000000790000001b18000011010000,Venom PS4 Arcade Joystick,a: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, +030000006f0e00000302000011010000,Victrix Pro 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:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, +030000006f0e00000702000011010000,Victrix Pro Fightstick 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,touchpad:b13,x:b0,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, +05000000434f4d4d414e440000000000,VX Gaming Command Series,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, +0000000058626f782033363020576900,Xbox 360 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, +030000005e0400001907000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +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, +030000005e0400009102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000a102000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000a102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000a102000030060000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00001503000000020000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000000010000,Xbox 360 EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000a102000014010000,Xbox 360 Receiver,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +0000000058626f782047616d65706100,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:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400000202000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +030000005e0400008e02000072050000,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, +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, +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, +030000005e040000ea02000000000000,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, +030000005e040000ea02000001030000,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, +050000005e040000e002000003090000,Xbox One 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 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, +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, +060000005e040000dd02000003020000,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, +050000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +030000005e040000ea02000011050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000ea02000015050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000ea02000017050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000ea0200000b050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000ea0200000d050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000ea02000016050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +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, +030000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000009050000,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, +030000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b00000f050000,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, +030000005e040000120b000011050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000014050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000015050000,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, +050000005e040000130b000007050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000130b000011050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000130b000017050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000005e040000130b000022050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +060000005e040000120b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000200b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000200b000023050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux, +03000000c0160000e105000001010000,XinMo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux, +030000005e0400008e02000020010000,XInput Adapter,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, +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,a: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,a: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, +03000000120c0000182e000011010000,Zeroplus 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, + +# Android +38653964633230666463343334313533,8BitDo Adapter,a:b0,b:b1,back:b15,dpdown:b12,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, +36666264316630653965636634386234,8BitDo Adapter 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,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, +38426974446f20417263616465205374,8BitDo Arcade Stick,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b5,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +61393962646434393836356631636132,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +64323139346131306233636562663738,8BitDo Arcade Stick,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +64643565386136613265663236636564,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, +33313433353539306634656436353432,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38426974446f20446f67626f6e65204d,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, +34343439373236623466343934376233,8BitDo FC30 Pro,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b28,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b29,righttrigger:b7,start:b5,x:b30,y:b2,platform:Android, +38426974446f204e4743204d6f646b69,8BitDo GameCube,a:b0,b:b2,back:b4,dpdown:b12,dpleft:b13,dpright:b14,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b18,paddle2:b17,rightshoulder:b15,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b1,y:b3,platform:Android, +38426974446f2038426974446f204c69,8BitDo Lite,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +30643332373663313263316637356631,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +38426974446f204c6974652032000000,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +62656331626461363634633735353032,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +38393936616436383062666232653338,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,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:b3,y:b2,platform:Android, +38426974446f204c6974652053450000,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +39356430616562366466646636643435,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,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:b3,y:b2,platform:Android, +05000000c82d000006500000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a5,rightshoulder:b10,righttrigger:a4,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000051060000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b3,y:b2,platform:Android, +32323161363037623637326438643634,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +33656266353630643966653238646264,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,start:b10,x:b19,y:b2,platform:Android, +38426974446f204d3330204d6f646b69,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39366630663062373237616566353437,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,platform:Android, +38426974446f204d6963726f2067616d,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, +61653365323561356263373333643266,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, +62613137616239666338343866326336,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, +33663431326134333366393233616633,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, +38426974446f204e3330204d6f646b69,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, +05000000c82d000015900000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000065280000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38323035343766666239373834336637,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, +38426974446f204e3634204d6f646b69,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, +32363135613966656338666638666237,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +35363534633333373639386466346631,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +38426974446f204e454f47454f204750,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39383963623932353561633733306334,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000000220000000900000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +050000002038000009000000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38313433643131656262306631373166,8BitDo P30,a:b0,b:b1,back:b15,dpdown: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, +38326536643339353865323063616339,8BitDo P30,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, +38426974446f2050333020636c617373,8BitDo P30,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, +35376664343164386333616535333434,8BitDo Pro 2,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,start:b10,x:b19,y:b2,platform:Android, +38426974446f2038426974446f205072,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +38426974446f2050726f203200000000,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,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, +61333362366131643730353063616330,8BitDo Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +62373739366537363166326238653463,8BitDo Pro 2,a:b1,b:b0,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,x:b3,y:b2,platform:Android, +38386464613034326435626130396565,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,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, +38426974446f2038426974446f205265,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,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, +66303230343038613365623964393766,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,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, +38426974446f20533330204d6f646b69,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +66316462353561376330346462316137,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,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: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:b2,y:b17,platform:Android, +61623334636338643233383735326439,8BitDo SFC30,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b31,start:b5,x:b30,y:b2,platform:Android, +05000000c82d000012900000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000062280000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +38316230613931613964356666353839,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +38426974446f20534e3330204d6f646b,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +65323563303231646531383162646335,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +35383531346263653330306238353131,8BitDo SN30 PP,a:b0,b:b1,back:b15,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, +05000000c82d000001600000ffff3f00,8BitDo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000002600000ffff0f00,8BitDo SN30 Pro Plus,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, +36653638656632326235346264663661,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,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:b3,y:b2,platform:Android, +38426974446f20534e33302050726f2b,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,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, +536f6e7920436f6d707574657220456e,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b15,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, +66306331643531333230306437353936,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,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:b3,y:b2,platform:Android, +050000002028000009000000ffff3f00,8BitDo SNES30,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,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,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +38426974646f205a65726f2047616d65,8BitDo Zero,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +33663434393362303033616630346337,8BitDo Zero 2,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +32333634613735616163326165323731,Amazon Luna Controller,a:b0,b:b1,back:b4,dpdown: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,x:b2,y:b3,platform:Android, +4c696e757820342e31392e3137322077,Anbernic Handheld,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Android, +417374726f2063697479206d696e6920,Astro City Mini,a:b23,b:b22,back:b29,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android, +35643263313264386134376362363435,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,start:b6,platform:Android, +32353831643566306563643065356239,Atari VCS Modern 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, +4f64696e20436f6e74726f6c6c657200,AYN Odin,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b14,dpright:b13,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, +32303165626138343962363666346165,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +38383337343564366131323064613561,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +34313430343161653665353737323365,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, +4875694a6961204a432d573031550000,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, +30363230653635633863366338623265,Evo VR,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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: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, +35383633353935396534393230616564,Google Stadia Controller,a:b0,b:b1,back:b15,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, +476f6f676c65204c4c43205374616469,Google Stadia Controller,a:b0,b:b1,back:b15,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, +5374616469614e3848532d6532633400,Google Stadia Controller,a:b0,b:b1,back:b15,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, +05000000d6020000e5890000dfff3f80,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,platform:Android, +66633030656131663837396562323935,Hori Battle,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +484f524920434f2e2c4c54442e203130,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +484f524920434f2e2c4c544420205041,Hori Gem Pad 3,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, +65656436646661313232656661616130,Hori PC Engine Mini Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b18,platform:Android, +31303433326562636431653534636633,Hori Real Arcade Pro 3,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +32656664353964393561366362333636,Hori Switch Split Pad Pro,a:b0,b:b1,back:b15,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, +30306539356238653637313730656134,HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +48797065726b696e2050616400000000,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,platform:Android, +62333331353131353034386136626636,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,platform:Android, +31306635363562663834633739396333,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +5368616e57616e202020202048797065,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b2,y:b3,platform:Android, +5553422c322d6178697320382d627574,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b3,y:b2,platform:Android, +64306137363261396266353433303531,InterAct GoPad,a:b24,b:b25,leftshoulder:b23,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,x:b21,y:b22,platform:Android, +532e542e442e20496e74657261637420,InterAct HammerHead FX,a:b23,b:b24,back:b30,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,leftstick:b22,lefttrigger:b28,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b25,righttrigger:b29,rightx:a2,righty:a3,start:b31,x:b20,y:b21,platform:Android, +65346535636333663931613264643164,Joy-Con,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, +4a6f792d436f6e20284c290000000000,Joy-Con (L),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, +38383665633039363066383334653465,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +39373064396565646338333134303131,Joy-Con (R),a:b1,b:b2,back:b5,leftstick:b8,leftx:a1~,lefty:a0,start:b6,x:b0,y:b3,platform:Android, +4a6f792d436f6e202852290000000000,Joy-Con (R),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +39656136363638323036303865326464,JYS Aapter,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,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:b0,y:b2,platform:Android, +64623163333561643339623235373232,Logitech F310,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, +35623364393661626231343866613337,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,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, +4c6f6769746563682047616d65706164,Logitech F710,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, +64396331333230326333313330336533,Logitech F710,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, +39653365373864633935383236363438,Logitech G Cloud,a:b0,b:b1,back:b15,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, +416d617a6f6e2047616d6520436f6e74,Luna Controller,a:b0,b:b1,back:b15,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, +4c756e612047616d6570616400000000,Luna 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, +30363066623539323534363639323363,Magic NS,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,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:b0,y:b2,platform:Android, +6d6179666c617368206c696d69746564,Mayflash GameCube Adapter,a:b22,b:b21,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a5,righty:a2,start:b30,x:b23,y:b24,platform:Android, +436f6e74726f6c6c6572000000000000,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +65666330633838383061313633326461,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, +37316565396364386635383230353365,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +4875694a696120205553422047616d65,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +535a4d792d706f776572204c54442043,Mayflash Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b31,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, +30653962643666303631376438373532,Mayflash Wii DolphinBar,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b0,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, +39346131396233376535393665363161,Mayflash Wii U Pro Adapter,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftstick:b31,lefttrigger:b27,rightshoulder:b26,rightstick:b0,righttrigger:b28,rightx:a0,righty:a1,start:b30,x:b21,y:b24,platform:Android, +31323564663862633234646330373138,Mega Drive,a:b23,b:b22,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android, +37333564393261653735306132613061,Mega Drive,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, +64363363336633363736393038313464,Mega Drive,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b9,x:b2,y:b3,platform:Android, +33323763323132376537376266393366,Microsoft Dual Strike,a:b24,b:b23,back:b25,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,platform:Android, +30306461613834333439303734316539,Microsoft SideWinder Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b20,lefttrigger:b9,rightshoulder:b19,righttrigger:b10,start:b17,x:b2,y:b3,platform:Android, +32386235353630393033393135613831,Microsoft 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, +4d4f42415041442050726f2d48440000,Mobapad Chitu HD,a:b0,b:b1,back:b15,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, +4d4f435554452d303533582d4d35312d,Mocute 053X,a:b0,b:b1,back:b15,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, +33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,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, +39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,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, +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, +31316661666466633938376335383661,Nintendo Switch Pro Controller,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,start:b6,x:b3,y:b2,platform:Android, +34323437396534643531326161633738,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +50726f20436f6e74726f6c6c65720000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b2,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b10,rightx:a2,righty:a3,start:b18,y:b3,platform:Android, +36326533353166323965623661303933,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, +4e363420436f6e74726f6c6c65720000,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, +534e455320436f6e74726f6c6c657200,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +64623863346133633561626136366634,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, +050000005509000003720000cf7f3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005509000010720000ffff3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005509000014720000df7f3f00,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, +050000005509000014720000df7f3f80,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,platform:Android, +37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown: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, +4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +39383335313438623439373538343266,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b16,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,x:b1,y:b19,platform:Android, +4f5559412047616d6520436f6e74726f,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b6,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b19,platform:Android, +506572666f726d616e63652044657369,PDP PS3 Rock Candy Controller,a:b1,b:b17,back:h0.2,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, +61653962353232366130326530363061,Pokken,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a3,righty:a2,start:b30,x:b24,y:b21,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, +536f6e7920504c415953544154494f4e,PS3 Controller,a:b0,b:b1,back:b15,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, +61363034663839376638653463633865,PS3 Controller,a:b0,b:b1,back:b15,dpdown:b12,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 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, +66383132326164626636313737373037,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000004c050000c405000000783f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +050000004c050000c4050000fffe3f80,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a3,rightx:a4,righty:a5,start:b16,x:b0,y:b2,platform:Android, +050000004c050000c4050000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000004c050000cc090000fffe3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +30303839663330346632363232623138,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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 Controller,a:b1,b:b16,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +31373231336561636235613666323035,PS4 Controller,a:b0,b:b1,back:b15,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, +31663838336334393132303338353963,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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 Controller,a:b0,b:b1,back:b15,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, +35643031303033326130316330353564,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, +37626233336235343937333961353732,PS4 Controller,a:b0,b:b1,back:b15,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, +37626464343430636562316661643863,PS4 Controller,a:b0,b:b1,back:b15,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, +38393161636261653636653532386639,PS4 Controller,a:b0,b:b1,back:b15,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, +63313733393535663339656564343962,PS4 Controller,a:b0,b:b1,back:b15,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, +63393662363836383439353064663939,PS4 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, +65366465656364636137653363376531,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +66613532303965383534396638613230,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +050000004c050000e60c0000fffe3f80,PS5 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a3,rightx:a4,righty:a5,start:b16,x:b2,y:b17,platform:Android, +050000004c050000e60c0000ffff3f00,PS5 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +32346465346533616263386539323932,PS5 Controller,a:b0,b:b1,back:b15,dpdown: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, +32633532643734376632656664383733,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +37363764353731323963323639666565,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, +64336263393933626535303339616332,Qanba 4RAF,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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: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, +05000000f8270000bf0b0000ffff3f00,Razer Kishi,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, +62653861643333663663383332396665,Razer Kishi,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +5a6869587520526574726f2042697420,Retro Bit Saturn Controller,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b26,rightshoulder:b27,righttrigger:b28,start:b30,x:b23,y:b24,platform:Android, +32417865732031314b6579732047616d,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +36313938306539326233393732613361,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, +526574726f466c616720576972656420,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b2,y:b3,platform:Android, +61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,platform:Android, +526574726f696420506f636b65742043,Retroid Pocket,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +582d426f7820436f6e74726f6c6c6572,Retroid Pocket,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +64633735616665613536653363336132,Retroid Pocket,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b19,paddle2:b20,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,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:b12,dpleft:b13,dpright:b14,dpup:b11,start:b6,platform:Android, +37393234373533633333323633646531,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android, +5365616c6965436f6d707574696e6720,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android, +526574726f5553422e636f6d20534e45,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,platform:Android, +64643037633038386238303966376137,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,platform:Android, +37656564346533643138636436356230,Rock Candy Switch Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, +33373336396634316434323337666361,RumblePad 2,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b24,platform:Android, +36363537303435333566386638366333,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +53616d73756e672047616d6520506164,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +66386565396238363534313863353065,Sanwa PlayOnline Mobile,a:b21,b:b22,back:b23,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b24,platform:Android, +32383165316333383766336338373261,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, +38613865396530353338373763623431,Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b10,rightshoulder:b20,righttrigger:b19,start:b17,x:b2,y:b3,platform:Android, +61316232336262373631343137633631,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,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: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,x:b2,y:b3,platform:Android, +66633132393363353531373465633064,SG H510,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,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:b3,y:b2,platform:Android, +38376662666661636265313264613039,SNES,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, +5346432f555342205061640000000000,SNES Adapter,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, +5553422047616d657061642000000000,SNES Controller,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, +62653335326261303663356263626339,Sony PlayStation Classic Controller,a:b19,b:b1,back:b17,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b3,rightshoulder:b10,righttrigger:b20,start:b18,x:b2,y:b0,platform:Android, +536f6e7920496e746572616374697665,Sony PlayStation Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +576972656c65737320436f6e74726f6c,Sony PlayStation Controller,a:b0,b:b1,back:b15,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, +63303964303462366136616266653561,Sony PSP,a:b21,b:b22,back:b27,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b23,y:b24,platform:Android, +63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,platform:Android, +35306436396437373135383665646464,SteelSeries Nimbus Plus,a:b0,b:b1,leftshoulder:b3,leftstick:b17,lefttrigger:b9,leftx:a0,rightshoulder:b20,rightstick:b18,righttrigger:b10,rightx:a2,x:b19,y:b2,platform:Android, +33313930373536613937326534303931,Taito Egret II Mini Control Panel,a:b25,b:b23,back:b27,guide:b30,leftx:a0,lefty:a1,rightshoulder:b21,righttrigger:b22,start:b28,x:b29,y:b24,platform:Android, +54475a20436f6e74726f6c6c65720000,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,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, +62363434353532386238336663643836,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,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, +37323236633763666465316365313236,THEC64 Joystick,a:b21,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b27,x:b23,y:b24,platform:Android, +38346162326232346533316164363336,THEGamepad,a:b23,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b24,y:b21,platform:Android, +050000004f0400000ed00000fffe3f00,Thrustmaster eSwap Pro Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +5477696e20555342204a6f7973746963,Twin Joystick,a:b22,b:b21,back:b28,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +31643365666432386133346639383937,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +30386438313564306161393537333663,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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 Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,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, +30396232393162346330326334636566,Xbox 360 Controller,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 Controller,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, +58626f782033363020576972656c6573,Xbox 360 Controller,a:b0,b:b1,back:b15,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, +65353331386662343338643939643636,Xbox 360 Controller,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 Controller,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, +47656e6572696320582d426f78207061,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +4d6963726f736f667420582d426f7820,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +64633436313965656664373634323364,Xbox Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e04000091020000ff073f00,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +050000005e04000091020000ff073f80,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000e00200000ffe3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, +050000005e040000e00200000ffe3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,platform:Android, +050000005e040000e0020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, +050000005e040000e0020000ffff3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,platform:Android, +050000005e040000fd020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +33356661323266333733373865656366,Xbox One Controller,a:b0,b:b1,back:b4,dpdown: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, +34356136633366613530316338376136,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +35623965373264386238353433656138,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +36616131643361333337396261666433,Xbox One Controller,a:b0,b:b1,back:b15,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, +58626f7820576972656c65737320436f,Xbox One Controller,a:b0,b:b1,back:b15,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, +65316262316265373335666131623538,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000000b000000783f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +050000005e040000000b000000783f80,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000050b0000ffff3f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000e002000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000ea02000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000fd020000ff7f3f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000120b000000783f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +050000005e040000120b000000783f80,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown: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, +65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,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, +050000001727000044310000ffff3f00,XiaoMi Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, + +# iOS +05000000ac0500000100000000006d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, +05000000ac050000010000004f066d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, +05000000ac05000001000000cf076d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, +05000000ac05000001000000df076d01,*,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, +05000000ac05000001000000ff076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, +05000000ac0500000200000000006d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS, +05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS, +05000000ac05000004000000a8986d04,8BitDo Micro,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,lefttrigger:b12,rightshoulder:b13,righttrigger:b14,start:b3,x:b6,y:b5,platform:iOS, +05000000ac05000004000000fd216d04,8BitDo Pro 2,a:b3,b:b2,back:b6,dpdown:b9,dpleft:b10,dpright:b11,dpup:b12,guide:b4,leftshoulder:b13,leftstick:b14,lefttrigger:+a2,leftx:a0,lefty:a1~,paddle1:b1,paddle2:b0,rightshoulder:b16,rightstick:b17,righttrigger:+a5,rightx:a3,righty:a4~,start:b5,x:b8,y:b7,platform:iOS, +05000000ac05000004000000209f6d04,8Bitdo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,leftstick:b12,lefttrigger:b13,leftx:a0,lefty:a1~,rightshoulder:b14,rightstick:b15,righttrigger:b16,rightx:a2,righty:a3~,start:b3,x:b6,y:b5,platform:iOS, +05000000ac050000040000003b8a6d04,8BitDo SN30 Pro+,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,leftstick:b12,lefttrigger:b13,leftx:a0,lefty:a1~,rightshoulder:b14,rightstick:b15,righttrigger:b16,rightx:a2,righty:a3~,start:b3,x:b6,y:b5,platform:iOS, +050000008a35000003010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, +050000008a35000004010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, +4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS, +4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS, +050000007e050000062000000f060000,Nintendo Switch Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,platform:iOS, +050000007e050000062000004f060000,Nintendo Switch Joy-Con (L),+leftx:h0.1,+lefty:h0.2,-leftx:h0.4,-lefty:h0.8,dpdown:b2,dpleft:b0,dpright:b3,dpup:b1,leftshoulder:b4,misc1:b6,rightshoulder:b5,platform:iOS, +050000007e05000008200000df070000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,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:b3,y:b2,platform:iOS, +050000007e0500000e200000df070000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,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:b3,y:b2,platform:iOS, +050000007e050000072000000f060000,Nintendo Switch Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,platform:iOS, +050000007e050000072000004f060000,Nintendo Switch Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,platform:iOS, +050000007e05000009200000df870000,Nintendo Switch Pro Controller,a:b1,b:b0,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:b3,y:b2,platform:iOS, +050000007e05000009200000ff870000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b3,y:b2,platform:iOS, +050000004c050000cc090000df070000,PS4 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, +050000004c050000cc090000df870001,PS4 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, +050000004c050000cc090000ff070000,PS4 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, +050000004c050000cc090000ff870001,PS4 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,touchpad:b11,x:b2,y:b3,platform:iOS, +050000004c050000cc090000ff876d01,PS4 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, +050000004c050000e60c0000df870000,PS5 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,touchpad:b10,x:b2,y:b3,platform:iOS, +050000004c050000e60c0000ff870000,PS5 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,touchpad:b11,x:b2,y:b3,platform:iOS, +05000000ac0500000300000000006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS, +05000000ac0500000300000043006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS, +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:iOS, +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/mac_controller_uart_bridge.py b/mac_controller_uart_bridge.py index 7cad9dd..0d0547c 100644 --- a/mac_controller_uart_bridge.py +++ b/mac_controller_uart_bridge.py @@ -20,6 +20,7 @@ import struct import threading import time from dataclasses import dataclass, field +from pathlib import Path from typing import Dict, List, Optional, Tuple import serial @@ -362,6 +363,12 @@ def build_arg_parser() -> argparse.ArgumentParser: default=UART_BAUD, help=f"UART baud rate (default {UART_BAUD}; must match switch-pico firmware)", ) + parser.add_argument( + "--sdl-mapping", + action="append", + default=[], + help="Path to an SDL2 controller mapping database (e.g. controllerdb.txt). Repeatable.", + ) return parser @@ -372,7 +379,24 @@ def main() -> None: deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) - sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER) + # Load bundled mapping plus any user-supplied mapping files. + default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" + mappings_to_load = [] + if default_mapping.exists(): + mappings_to_load.append(str(default_mapping)) + mappings_to_load.extend(args.sdl_mapping) + for mapping_path in mappings_to_load: + try: + loaded = sdl2.SDL_GameControllerAddMappingsFromFile(mapping_path.encode()) + console = Console() + console.print(f"[green]Loaded {loaded} SDL mapping(s) from {mapping_path}[/green]") + except Exception as exc: + console = Console() + console.print(f"[red]Failed to load SDL mapping {mapping_path}: {exc}[/red]") + + sdl2.SDL_SetHint(sdl2.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, b"1") + if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_JOYSTICK) != 0: + parser.error(f"SDL init failed: {sdl2.SDL_GetError().decode(errors='ignore')}") contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] mapping_by_index: Dict[int, str] = {} @@ -390,6 +414,10 @@ def main() -> None: console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") controller_indices.append(index) controller_names[index] = name_str + else: + name = sdl2.SDL_JoystickNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print(f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]") mappings = list(args.map) if args.interactive: @@ -442,6 +470,11 @@ def main() -> None: # Open currently connected controllers that match the mapping. for index, port in mappings: if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): + # Try to turn non-GameController into a controller via hint reload. + if index < sdl2.SDL_NumJoysticks(): + name = sdl2.SDL_JoystickNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print(f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") continue try: controller, instance_id = open_controller(index) From 02bce399a510d086cf39e458bb3b7f67e6f6f4c3 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 21 Nov 2025 13:23:43 -0700 Subject: [PATCH 019/134] Add color config --- controller_color_config.h | 25 +++++++++++++++++++++++++ mac_controller_uart_bridge.py | 5 +++++ switch_pro_driver.cpp | 33 +++++++++++++++++++++++++++++---- 3 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 controller_color_config.h diff --git a/controller_color_config.h b/controller_color_config.h new file mode 100644 index 0000000..4abdf51 --- /dev/null +++ b/controller_color_config.h @@ -0,0 +1,25 @@ +// Optional override for Switch Pro colour fields. +// Copy/modify the values below and rebuild to change how the controller appears on the Switch. +// Each value is an 8-bit RGB component. + +#pragma once + +// Body shell colour +#define SWITCH_COLOR_BODY_R 0x1B +#define SWITCH_COLOR_BODY_G 0x1B +#define SWITCH_COLOR_BODY_B 0x1D + +// Face/button cluster colour +#define SWITCH_COLOR_BUTTON_R 0xFF +#define SWITCH_COLOR_BUTTON_G 0xFF +#define SWITCH_COLOR_BUTTON_B 0xFF + +// Left grip colour +#define SWITCH_COLOR_LEFT_GRIP_R 0x00 +#define SWITCH_COLOR_LEFT_GRIP_G 0x89 +#define SWITCH_COLOR_LEFT_GRIP_B 0xEB + +// Right grip colour +#define SWITCH_COLOR_RIGHT_GRIP_R 0x00 +#define SWITCH_COLOR_RIGHT_GRIP_G 0x89 +#define SWITCH_COLOR_RIGHT_GRIP_B 0xEB diff --git a/mac_controller_uart_bridge.py b/mac_controller_uart_bridge.py index 0d0547c..1716b49 100644 --- a/mac_controller_uart_bridge.py +++ b/mac_controller_uart_bridge.py @@ -581,6 +581,11 @@ def main() -> None: contexts[instance_id] = ctx elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: instance_id = event.cdevice.which + # ctx = contexts.get(instance_id) + # if ctx and sdl2.SDL_GameControllerGetAttached(ctx.controller): + # # Spurious detach; ignore. + # console.print(f"[yellow]Ignoring spurious remove for controller {instance_id}[/yellow]") + # continue ctx = contexts.pop(instance_id, None) if ctx: console.print(f"[yellow]Controller {instance_id} removed[/yellow]") diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index 5e71e71..138ba8f 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -44,6 +44,31 @@ static uint8_t input_mode = 0x30; static bool is_imu_enabled = false; static bool is_vibration_enabled = false; +// Optional compile-time colour override (body/buttons/grips). +#if __has_include("controller_color_config.h") +#include "controller_color_config.h" +#endif +#ifndef SWITCH_COLOR_BODY_R +#define SWITCH_COLOR_BODY_R 0x1B +#define SWITCH_COLOR_BODY_G 0x1B +#define SWITCH_COLOR_BODY_B 0x1D +#endif +#ifndef SWITCH_COLOR_BUTTON_R +#define SWITCH_COLOR_BUTTON_R 0xFF +#define SWITCH_COLOR_BUTTON_G 0xFF +#define SWITCH_COLOR_BUTTON_B 0xFF +#endif +#ifndef SWITCH_COLOR_LEFT_GRIP_R +#define SWITCH_COLOR_LEFT_GRIP_R 0xEC +#define SWITCH_COLOR_LEFT_GRIP_G 0x00 +#define SWITCH_COLOR_LEFT_GRIP_B 0x8C +#endif +#ifndef SWITCH_COLOR_RIGHT_GRIP_R +#define SWITCH_COLOR_RIGHT_GRIP_R 0xEC +#define SWITCH_COLOR_RIGHT_GRIP_G 0x00 +#define SWITCH_COLOR_RIGHT_GRIP_B 0x8C +#endif + static uint16_t leftMinX, leftMinY; static uint16_t leftCenX, leftCenY; static uint16_t leftMaxX, leftMaxY; @@ -91,16 +116,16 @@ static const uint8_t factory_config_data[0xEFF] = { 0xFF, // body color - 0x1B, 0x1B, 0x1D, + SWITCH_COLOR_BODY_R, SWITCH_COLOR_BODY_G, SWITCH_COLOR_BODY_B, // button color - 0xFF, 0xFF, 0xFF, + SWITCH_COLOR_BUTTON_R, SWITCH_COLOR_BUTTON_G, SWITCH_COLOR_BUTTON_B, // left grip color - 0xEC, 0x00, 0x8C, + SWITCH_COLOR_LEFT_GRIP_R, SWITCH_COLOR_LEFT_GRIP_G, SWITCH_COLOR_LEFT_GRIP_B, // right grip color - 0xEC, 0x00, 0x8C, + SWITCH_COLOR_RIGHT_GRIP_R, SWITCH_COLOR_RIGHT_GRIP_G, SWITCH_COLOR_RIGHT_GRIP_B, 0x01, From e8b8d6d670d6c04eb6928e604c614c8710871905 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 21 Nov 2025 13:23:43 -0700 Subject: [PATCH 020/134] Add color config --- controller_color_config.h | 25 +++++++++++++++++++++++++ mac_controller_uart_bridge.py | 5 +++++ switch_pro_driver.cpp | 33 +++++++++++++++++++++++++++++---- 3 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 controller_color_config.h diff --git a/controller_color_config.h b/controller_color_config.h new file mode 100644 index 0000000..4abdf51 --- /dev/null +++ b/controller_color_config.h @@ -0,0 +1,25 @@ +// Optional override for Switch Pro colour fields. +// Copy/modify the values below and rebuild to change how the controller appears on the Switch. +// Each value is an 8-bit RGB component. + +#pragma once + +// Body shell colour +#define SWITCH_COLOR_BODY_R 0x1B +#define SWITCH_COLOR_BODY_G 0x1B +#define SWITCH_COLOR_BODY_B 0x1D + +// Face/button cluster colour +#define SWITCH_COLOR_BUTTON_R 0xFF +#define SWITCH_COLOR_BUTTON_G 0xFF +#define SWITCH_COLOR_BUTTON_B 0xFF + +// Left grip colour +#define SWITCH_COLOR_LEFT_GRIP_R 0x00 +#define SWITCH_COLOR_LEFT_GRIP_G 0x89 +#define SWITCH_COLOR_LEFT_GRIP_B 0xEB + +// Right grip colour +#define SWITCH_COLOR_RIGHT_GRIP_R 0x00 +#define SWITCH_COLOR_RIGHT_GRIP_G 0x89 +#define SWITCH_COLOR_RIGHT_GRIP_B 0xEB diff --git a/mac_controller_uart_bridge.py b/mac_controller_uart_bridge.py index 0d0547c..1716b49 100644 --- a/mac_controller_uart_bridge.py +++ b/mac_controller_uart_bridge.py @@ -581,6 +581,11 @@ def main() -> None: contexts[instance_id] = ctx elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: instance_id = event.cdevice.which + # ctx = contexts.get(instance_id) + # if ctx and sdl2.SDL_GameControllerGetAttached(ctx.controller): + # # Spurious detach; ignore. + # console.print(f"[yellow]Ignoring spurious remove for controller {instance_id}[/yellow]") + # continue ctx = contexts.pop(instance_id, None) if ctx: console.print(f"[yellow]Controller {instance_id} removed[/yellow]") diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index 5e71e71..138ba8f 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -44,6 +44,31 @@ static uint8_t input_mode = 0x30; static bool is_imu_enabled = false; static bool is_vibration_enabled = false; +// Optional compile-time colour override (body/buttons/grips). +#if __has_include("controller_color_config.h") +#include "controller_color_config.h" +#endif +#ifndef SWITCH_COLOR_BODY_R +#define SWITCH_COLOR_BODY_R 0x1B +#define SWITCH_COLOR_BODY_G 0x1B +#define SWITCH_COLOR_BODY_B 0x1D +#endif +#ifndef SWITCH_COLOR_BUTTON_R +#define SWITCH_COLOR_BUTTON_R 0xFF +#define SWITCH_COLOR_BUTTON_G 0xFF +#define SWITCH_COLOR_BUTTON_B 0xFF +#endif +#ifndef SWITCH_COLOR_LEFT_GRIP_R +#define SWITCH_COLOR_LEFT_GRIP_R 0xEC +#define SWITCH_COLOR_LEFT_GRIP_G 0x00 +#define SWITCH_COLOR_LEFT_GRIP_B 0x8C +#endif +#ifndef SWITCH_COLOR_RIGHT_GRIP_R +#define SWITCH_COLOR_RIGHT_GRIP_R 0xEC +#define SWITCH_COLOR_RIGHT_GRIP_G 0x00 +#define SWITCH_COLOR_RIGHT_GRIP_B 0x8C +#endif + static uint16_t leftMinX, leftMinY; static uint16_t leftCenX, leftCenY; static uint16_t leftMaxX, leftMaxY; @@ -91,16 +116,16 @@ static const uint8_t factory_config_data[0xEFF] = { 0xFF, // body color - 0x1B, 0x1B, 0x1D, + SWITCH_COLOR_BODY_R, SWITCH_COLOR_BODY_G, SWITCH_COLOR_BODY_B, // button color - 0xFF, 0xFF, 0xFF, + SWITCH_COLOR_BUTTON_R, SWITCH_COLOR_BUTTON_G, SWITCH_COLOR_BUTTON_B, // left grip color - 0xEC, 0x00, 0x8C, + SWITCH_COLOR_LEFT_GRIP_R, SWITCH_COLOR_LEFT_GRIP_G, SWITCH_COLOR_LEFT_GRIP_B, // right grip color - 0xEC, 0x00, 0x8C, + SWITCH_COLOR_RIGHT_GRIP_R, SWITCH_COLOR_RIGHT_GRIP_G, SWITCH_COLOR_RIGHT_GRIP_B, 0x01, From 8b7a6d5cb2d0c33f74eff160041431ae0d5130f0 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 21 Nov 2025 15:00:17 -0700 Subject: [PATCH 021/134] Update with docs and rename uart file --- README.md | 77 +++++++++++++++++++ ...art_bridge.py => controller_uart_bridge.py | 0 2 files changed, 77 insertions(+) create mode 100644 README.md rename mac_controller_uart_bridge.py => controller_uart_bridge.py (100%) diff --git a/README.md b/README.md new file mode 100644 index 0000000..c25bc29 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# Switch Pico Controller Bridge + +Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a host bridge that forwards real gamepad input over UART (with rumble round-trip). + +## What you get +- **Firmware** (`switch-pico.cpp` + `switch_pro_driver.*`): acts as a wired Switch Pro. Takes controller reports over UART1 and passes rumble from the Switch back over UART. +- **Python bridge** (`controller_uart_bridge.py`): reads SDL2 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux). +- **Optional Rust bridge** (`fast_uart_bridge/`): higher‑performance alternative if the Python bridge has rumble latency. +- **Colour override** (`controller_color_config.h`): compile‑time RGB overrides for body/buttons/grips as seen by the Switch. + +## Hardware wiring (Pico) +- UART1 pins (fixed in firmware): + - **TX**: GPIO4 (Pico pin 6) → RX of your USB‑serial adapter. + - **RX**: GPIO5 (Pico pin 7) → TX of your USB‑serial adapter. + - **GND**: common ground between Pico and adapter. +- Baud rate: **1,000,000** (default). Some adapters only handle 500,000; both bridges accept a `--baud` flag. +- Keep logic at 3.3V; do not feed 5V UART into the Pico. + +## Building and flashing firmware +Prereqs: Pico SDK + CMake toolchain set up. +```sh +cmake -S . -B build -DSWITCH_PICO_AUTOTEST=OFF -DSWITCH_PICO_LOG=OFF +cmake --build build -j +``` +Flash the UF2 to the Pico (e.g., bootsel + drag-drop or `picotool load`). +Flags: +- `SWITCH_PICO_AUTOTEST` (default ON in some configs): disable autopilot/test replay with `OFF`. +- `SWITCH_PICO_LOG`: enable/disable UART logging on the Pico. + +### Changing controller colours +Edit `controller_color_config.h` (RGB values) and rebuild; the Switch will show the new colours on reconnect. + +## Python bridge (recommended) +Works on macOS, Windows, Linux. Uses SDL2 + pyserial. + +### Install dependencies with uv (or pip) +```sh +# from repo root +uv venv .venv +uv pip install pyserial pysdl2 +``` +- SDL2 runtime: install via your OS package manager (macOS: `brew install sdl2`; Windows: place `SDL2.dll` on PATH or next to the script; Linux: `sudo apt install libsdl2-2.0-0` or equivalent). + +### Run +```sh +source .venv/bin/activate # or .venv\Scripts\activate on Windows +python controller_uart_bridge.py --interactive +``` +Options: +- `--map index:PORT` (repeatable) to pin controller index to serial (e.g., `--map 0:/dev/cu.usbserial-0001` or `--map 0:COM5`). +- `--ports PORTS...` or `--interactive` for auto/interactive pairing. +- `--baud 900000` (default 900,000; use `500000` if your adapter can’t do 900K). +- `--frequency 1000` to send at 1 kHz. +- `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). + +Hot‑plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. + +### macOS tips +- Ensure the USB‑serial adapter shows up (use `/dev/cu.usb*` for TX). +- Some controllers’ Guide/Home buttons are intercepted by macOS; using XInput/DInput mode or disabling Steam’s controller handling helps. + +### Windows tips +- Use `COMx` for ports (e.g., `COM5`). Auto‑detect lists COM ports. +- Ensure SDL2.dll is on PATH or alongside the script. + +### Linux tips +- You may need udev permissions for `/dev/ttyUSB*`/`/dev/ttyACM*` (add user to `dialout`/`uucp` or use `udev` rules). + +## Rust bridge (optional) +Note: Rust toolchain not currently present in this setup. If you install Rust + SDL2 dev libraries, an experimental Rust bridge lives in `fast_uart_bridge/`. + +## Troubleshooting +- **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, Switch sees a “Pro Controller”. +- **Rumble lag**: increase baud to 900,000 (default) or 1,000,000 if your adapter supports it; otherwise increase `--frequency`. +- **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `controller_uart_bridge.py` if needed. +- **Guide/Home triggers system menu (macOS)**: try different controller mode (XInput/DInput), disable Steam overlay/controller support, or connect wired. +- **SDL can’t see controller**: load `controller_db/gamecontrollerdb.txt` (default), add your own mapping, or try a different mode on the pad (e.g., XInput). diff --git a/mac_controller_uart_bridge.py b/controller_uart_bridge.py similarity index 100% rename from mac_controller_uart_bridge.py rename to controller_uart_bridge.py From 92af05227993c0dc220d25224b199a992425fee8 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 21 Nov 2025 15:00:17 -0700 Subject: [PATCH 022/134] Update with docs and rename uart file --- README.md | 77 +++++++++++++++++++ ...art_bridge.py => controller_uart_bridge.py | 0 2 files changed, 77 insertions(+) create mode 100644 README.md rename mac_controller_uart_bridge.py => controller_uart_bridge.py (100%) diff --git a/README.md b/README.md new file mode 100644 index 0000000..c25bc29 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# Switch Pico Controller Bridge + +Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a host bridge that forwards real gamepad input over UART (with rumble round-trip). + +## What you get +- **Firmware** (`switch-pico.cpp` + `switch_pro_driver.*`): acts as a wired Switch Pro. Takes controller reports over UART1 and passes rumble from the Switch back over UART. +- **Python bridge** (`controller_uart_bridge.py`): reads SDL2 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux). +- **Optional Rust bridge** (`fast_uart_bridge/`): higher‑performance alternative if the Python bridge has rumble latency. +- **Colour override** (`controller_color_config.h`): compile‑time RGB overrides for body/buttons/grips as seen by the Switch. + +## Hardware wiring (Pico) +- UART1 pins (fixed in firmware): + - **TX**: GPIO4 (Pico pin 6) → RX of your USB‑serial adapter. + - **RX**: GPIO5 (Pico pin 7) → TX of your USB‑serial adapter. + - **GND**: common ground between Pico and adapter. +- Baud rate: **1,000,000** (default). Some adapters only handle 500,000; both bridges accept a `--baud` flag. +- Keep logic at 3.3V; do not feed 5V UART into the Pico. + +## Building and flashing firmware +Prereqs: Pico SDK + CMake toolchain set up. +```sh +cmake -S . -B build -DSWITCH_PICO_AUTOTEST=OFF -DSWITCH_PICO_LOG=OFF +cmake --build build -j +``` +Flash the UF2 to the Pico (e.g., bootsel + drag-drop or `picotool load`). +Flags: +- `SWITCH_PICO_AUTOTEST` (default ON in some configs): disable autopilot/test replay with `OFF`. +- `SWITCH_PICO_LOG`: enable/disable UART logging on the Pico. + +### Changing controller colours +Edit `controller_color_config.h` (RGB values) and rebuild; the Switch will show the new colours on reconnect. + +## Python bridge (recommended) +Works on macOS, Windows, Linux. Uses SDL2 + pyserial. + +### Install dependencies with uv (or pip) +```sh +# from repo root +uv venv .venv +uv pip install pyserial pysdl2 +``` +- SDL2 runtime: install via your OS package manager (macOS: `brew install sdl2`; Windows: place `SDL2.dll` on PATH or next to the script; Linux: `sudo apt install libsdl2-2.0-0` or equivalent). + +### Run +```sh +source .venv/bin/activate # or .venv\Scripts\activate on Windows +python controller_uart_bridge.py --interactive +``` +Options: +- `--map index:PORT` (repeatable) to pin controller index to serial (e.g., `--map 0:/dev/cu.usbserial-0001` or `--map 0:COM5`). +- `--ports PORTS...` or `--interactive` for auto/interactive pairing. +- `--baud 900000` (default 900,000; use `500000` if your adapter can’t do 900K). +- `--frequency 1000` to send at 1 kHz. +- `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). + +Hot‑plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. + +### macOS tips +- Ensure the USB‑serial adapter shows up (use `/dev/cu.usb*` for TX). +- Some controllers’ Guide/Home buttons are intercepted by macOS; using XInput/DInput mode or disabling Steam’s controller handling helps. + +### Windows tips +- Use `COMx` for ports (e.g., `COM5`). Auto‑detect lists COM ports. +- Ensure SDL2.dll is on PATH or alongside the script. + +### Linux tips +- You may need udev permissions for `/dev/ttyUSB*`/`/dev/ttyACM*` (add user to `dialout`/`uucp` or use `udev` rules). + +## Rust bridge (optional) +Note: Rust toolchain not currently present in this setup. If you install Rust + SDL2 dev libraries, an experimental Rust bridge lives in `fast_uart_bridge/`. + +## Troubleshooting +- **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, Switch sees a “Pro Controller”. +- **Rumble lag**: increase baud to 900,000 (default) or 1,000,000 if your adapter supports it; otherwise increase `--frequency`. +- **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `controller_uart_bridge.py` if needed. +- **Guide/Home triggers system menu (macOS)**: try different controller mode (XInput/DInput), disable Steam overlay/controller support, or connect wired. +- **SDL can’t see controller**: load `controller_db/gamecontrollerdb.txt` (default), add your own mapping, or try a different mode on the pad (e.g., XInput). diff --git a/mac_controller_uart_bridge.py b/controller_uart_bridge.py similarity index 100% rename from mac_controller_uart_bridge.py rename to controller_uart_bridge.py From f6409ba9058ba36dbfcdd1f12eb7fa013fd4f4d1 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 21 Nov 2025 15:46:06 -0700 Subject: [PATCH 023/134] Controller tweaks --- controller_uart_bridge.py | 212 +++++++++++++++++++++++++++----------- 1 file changed, 149 insertions(+), 63 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 1716b49..84152e7 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -37,6 +37,9 @@ UART_HEADER = 0xAA RUMBLE_HEADER = 0xBB RUMBLE_TYPE_RUMBLE = 0x01 UART_BAUD = 900000 +RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off +RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this +RUMBLE_MIN_ACTIVE = 0.50 # below this, rumble is treated as off/noise class SwitchButton: @@ -269,8 +272,9 @@ def decode_rumble(payload: bytes) -> Tuple[float, float]: if payload == b"\x00\x01\x40\x40\x00\x01\x40\x40": return 0.0, 0.0 # Rumble amp is 10 bits across bytes 0/1 and 4/5. - left_raw = ((payload[1] & 0x03) << 8) | payload[0] - right_raw = ((payload[5] & 0x03) << 8) | payload[4] + # Switch format is right rumble first, then left rumble (4 bytes each). + right_raw = ((payload[1] & 0x03) << 8) | payload[0] + left_raw = ((payload[5] & 0x03) << 8) | payload[4] if left_raw < 8 and right_raw < 8: return 0.0, 0.0 left = min(max(left_raw / 1023.0, 0.0), 1.0) @@ -278,19 +282,20 @@ def decode_rumble(payload: bytes) -> Tuple[float, float]: return left, right -def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> None: +def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> float: left_norm, right_norm = decode_rumble(payload) max_norm = max(left_norm, right_norm) # Treat small rumble as "off" to avoid idle buzz. - if max_norm < 0.40: + if max_norm < RUMBLE_MIN_ACTIVE: sdl2.SDL_GameControllerRumble(controller, 0, 0, 0) - return + return 0.0 # Attenuate to feel closer to a real controller; cap at ~25% strength. - scale = 0.40 - left = int(min(1.0, left_norm * scale) * 0xFFFF) - right = int(min(1.0, right_norm * scale) * 0xFFFF) + scale = 0.60 + low = int(min(1.0, left_norm * scale) * 0xFFFF) # SDL: low_frequency_rumble + high = int(min(1.0, right_norm * scale) * 0xFFFF) # SDL: high_frequency_rumble duration = 10 - sdl2.SDL_GameControllerRumble(controller, left, right, duration) + sdl2.SDL_GameControllerRumble(controller, low, high, duration) + return max_norm @dataclass @@ -298,13 +303,17 @@ class ControllerContext: controller: sdl2.SDL_GameController instance_id: int controller_index: int - port: str + port: Optional[str] uart: Optional[PicoUART] report: SwitchReport = field(default_factory=SwitchReport) dpad: Dict[str, bool] = field(default_factory=lambda: {"up": False, "down": False, "left": False, "right": False}) last_trigger_state: Dict[str, bool] = field(default_factory=lambda: {"left": False, "right": False}) last_send: float = 0.0 last_reopen_attempt: float = 0.0 + last_rumble: float = 0.0 + last_rumble_change: float = 0.0 + last_rumble_energy: float = 0.0 + rumble_active: bool = False def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: @@ -407,6 +416,11 @@ def main() -> None: controller_count = sdl2.SDL_NumJoysticks() if controller_count < 0: parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") + auto_pairing_enabled = not args.map and not args.interactive + auto_discover_ports = auto_pairing_enabled and not args.ports + available_ports: List[str] = [] + auto_assigned_indices: set[int] = set() + include_non_usb = args.all_ports or False for index in range(controller_count): if sdl2.SDL_IsGameController(index): name = sdl2.SDL_GameControllerNameForIndex(index) @@ -423,70 +437,65 @@ def main() -> None: if args.interactive: if not controller_indices: parser.error("No controllers detected for interactive pairing.") - discovered = discover_ports(include_non_usb=args.all_ports or False) + discovered = discover_ports(include_non_usb=include_non_usb) if not discovered: parser.error("No UART devices found for interactive pairing.") mappings = interactive_pairing(console, controller_names, discovered) if not mappings: parser.error("No controller-to-UART mappings were selected.") - elif not mappings: + elif auto_pairing_enabled: if args.ports: - validated = list(args.ports) - if not controller_indices: - parser.error("No controllers available to pair.") - pair_count = min(len(controller_indices), len(validated)) - if pair_count == 0: - parser.error("No UART ports specified.") - if len(validated) < len(controller_indices): - console.print( - f"[yellow]Warning: only {len(validated)} UART(s) provided; pairing first {pair_count} controllers.[/yellow]" - ) - mappings = list((controller_indices[i], validated[i]) for i in range(pair_count)) - console.print("[green]Paired controllers to specified UART ports:[/green]") - for idx, port in mappings: - console.print(f" Controller {idx} -> {port}") + available_ports.extend(list(args.ports)) + console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") else: - discovered = discover_ports(include_non_usb=args.all_ports or False) - if not discovered: - parser.error("No UART devices detected automatically.") - pair_count = min(len(controller_indices), len(discovered)) - if pair_count == 0: - parser.error("No controllers detected to pair.") - if len(discovered) < len(controller_indices): - console.print( - f"[yellow]Warning: detected {len(discovered)} UART(s) but {len(controller_indices)} controller(s); pairing first {pair_count}.[/yellow]" - ) - mappings = list((controller_indices[i], discovered[i]["device"]) for i in range(pair_count)) - console.print("[green]Auto-detected UARTs:[/green]") - for info in discovered: - console.print(f" {info['device']} ({info['description']})") - console.print("[green]Paired controllers to detected UARTs:[/green]") - for idx, port in mappings: - console.print(f" Controller {idx} -> {port}") + discovered = discover_ports(include_non_usb=include_non_usb) + if discovered: + available_ports.extend(info["device"] for info in discovered) + console.print("[green]Auto-detected UARTs:[/green]") + for info in discovered: + console.print(f" {info['device']} ({info['description']})") + else: + console.print("[yellow]No UART devices detected yet; waiting for hotplug...[/yellow]") for index, port in mappings: mapping_by_index[index] = port - # Open currently connected controllers that match the mapping. - for index, port in mappings: + def assign_port_for_index(idx: int) -> Optional[str]: + if idx in mapping_by_index: + return mapping_by_index[idx] + if not auto_pairing_enabled: + return None + if not available_ports: + return None + port_choice = available_ports.pop(0) + mapping_by_index[idx] = port_choice + auto_assigned_indices.add(idx) + console.print(f"[green]Auto-paired controller {idx} to {port_choice}[/green]") + return port_choice + + # Open currently connected controllers that we can pair. + for index in controller_indices: if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): - # Try to turn non-GameController into a controller via hint reload. - if index < sdl2.SDL_NumJoysticks(): - name = sdl2.SDL_JoystickNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) - console.print(f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") + name = sdl2.SDL_JoystickNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print(f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") + continue + port = assign_port_for_index(index) + if port is None and not auto_pairing_enabled: continue try: controller, instance_id = open_controller(index) except Exception as exc: console.print(f"[red]Failed to open controller {index}: {exc}[/red]") continue - uart = try_open_uart(port, args.baud) + uart = try_open_uart(port, args.baud) if port else None if uart: uarts.append(uart) console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") - else: + elif port: console.print(f"[yellow]Controller {index} ({instance_id}) waiting for UART {port}[/yellow]") + else: + console.print(f"[yellow]Controller {index} ({instance_id}) connected; waiting for an available UART[/yellow]") ctx = ControllerContext( controller=controller, instance_id=instance_id, @@ -496,10 +505,51 @@ def main() -> None: ) contexts[instance_id] = ctx - if not contexts and not mapping_by_index: - parser.error("No controllers opened. Check --map/--ports/--interactive values.") + if not contexts: + console.print("[yellow]No controllers opened; waiting for hotplug events...[/yellow]") + + def ports_in_use() -> set: + used = set(mapping_by_index.values()) + used.update(ctx.port for ctx in contexts.values() if ctx.port) + return used + + def discover_new_ports() -> None: + if not auto_discover_ports: + return + discovered = discover_ports(include_non_usb=include_non_usb) + in_use = ports_in_use() + for info in discovered: + path = info["device"] + if path in in_use or path in available_ports: + continue + available_ports.append(path) + console.print(f"[green]Discovered UART {path} ({info['description']}); available for pairing.[/green]") + + def pair_waiting_contexts() -> None: + for ctx in list(contexts.values()): + if ctx.port is not None: + continue + port_choice = assign_port_for_index(ctx.controller_index) + if port_choice is None: + continue + ctx.port = port_choice + uart = try_open_uart(port_choice, args.baud) + ctx.last_reopen_attempt = time.monotonic() + if uart: + uarts.append(uart) + ctx.uart = uart + console.print( + f"[green]Controller {ctx.controller_index} ({ctx.instance_id}) paired to {port_choice}[/green]" + ) + else: + ctx.uart = None + console.print( + f"[yellow]Controller {ctx.controller_index} ({ctx.instance_id}) waiting for UART {port_choice}[/yellow]" + ) event = sdl2.SDL_Event() + port_scan_interval = 2.0 + last_port_scan = time.monotonic() running = True while running: while sdl2.SDL_PollEvent(event): @@ -553,24 +603,31 @@ def main() -> None: ctx.report.hat = dpad_to_hat(ctx.dpad) elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: idx = event.cdevice.which - port = mapping_by_index.get(idx) - if port is None: + if any(c.controller_index == idx for c in contexts.values()): continue - # Avoid duplicate opens for already connected instance IDs. - already = any(c.controller_index == idx for c in contexts.values()) - if already: + port = assign_port_for_index(idx) + if port is None and not auto_pairing_enabled: + continue + if idx >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(idx): + name = sdl2.SDL_JoystickNameForIndex(idx) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print( + f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" + ) continue try: controller, instance_id = open_controller(idx) except Exception as exc: console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") continue - uart = try_open_uart(port, args.baud) + uart = try_open_uart(port, args.baud) if port else None if uart: uarts.append(uart) console.print(f"[green]Controller {idx} ({instance_id}) paired to {port}[/green]") - else: + elif port: console.print(f"[yellow]Controller {idx} ({instance_id}) waiting for UART {port}[/yellow]") + else: + console.print(f"[yellow]Controller {idx} ({instance_id}) connected; waiting for an available UART[/yellow]") ctx = ControllerContext( controller=controller, instance_id=instance_id, @@ -589,12 +646,24 @@ def main() -> None: ctx = contexts.pop(instance_id, None) if ctx: console.print(f"[yellow]Controller {instance_id} removed[/yellow]") + if ctx.controller_index in auto_assigned_indices: + freed = mapping_by_index.pop(ctx.controller_index, None) + auto_assigned_indices.discard(ctx.controller_index) + if freed and freed not in available_ports: + available_ports.append(freed) + console.print(f"[cyan]Released UART {freed} back to pool[/cyan]") sdl2.SDL_GameControllerClose(ctx.controller) now = time.monotonic() + if now - last_port_scan > port_scan_interval: + discover_new_ports() + last_port_scan = now + pair_waiting_contexts() + else: + pair_waiting_contexts() for ctx in list(contexts.values()): # Reconnect UART if needed. - if ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: + if ctx.port and ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: ctx.last_reopen_attempt = now uart = try_open_uart(ctx.port, args.baud) if uart: @@ -610,14 +679,31 @@ def main() -> None: # Poll rumble quickly while we have the port. payload = ctx.uart.read_rumble_payload() if payload: - apply_rumble(ctx.controller, payload) + energy = apply_rumble(ctx.controller, payload) + ctx.rumble_active = energy >= RUMBLE_MIN_ACTIVE + if ctx.rumble_active and energy != ctx.last_rumble_energy: + ctx.last_rumble_change = now + ctx.last_rumble_energy = energy + ctx.last_rumble = now + elif ctx.rumble_active and (now - ctx.last_rumble) > RUMBLE_IDLE_TIMEOUT: + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 + elif ctx.rumble_active and (now - ctx.last_rumble_change) > RUMBLE_STUCK_TIMEOUT: + # Guard against a stream of tiny-but-nonzero rumble packets that never decay. + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 except SerialException as exc: console.print(f"[yellow]UART {ctx.port} disconnected: {exc}[/yellow]") try: ctx.uart.close() except Exception: pass + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) ctx.uart = None + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 ctx.last_reopen_attempt = now except Exception as exc: console.print(f"[red]UART error on {ctx.port}: {exc}[/red]") From 9105f8285b9c66026a378448cd21b04623b7fa64 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 21 Nov 2025 15:46:06 -0700 Subject: [PATCH 024/134] Controller tweaks --- controller_uart_bridge.py | 212 +++++++++++++++++++++++++++----------- 1 file changed, 149 insertions(+), 63 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 1716b49..84152e7 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -37,6 +37,9 @@ UART_HEADER = 0xAA RUMBLE_HEADER = 0xBB RUMBLE_TYPE_RUMBLE = 0x01 UART_BAUD = 900000 +RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off +RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this +RUMBLE_MIN_ACTIVE = 0.50 # below this, rumble is treated as off/noise class SwitchButton: @@ -269,8 +272,9 @@ def decode_rumble(payload: bytes) -> Tuple[float, float]: if payload == b"\x00\x01\x40\x40\x00\x01\x40\x40": return 0.0, 0.0 # Rumble amp is 10 bits across bytes 0/1 and 4/5. - left_raw = ((payload[1] & 0x03) << 8) | payload[0] - right_raw = ((payload[5] & 0x03) << 8) | payload[4] + # Switch format is right rumble first, then left rumble (4 bytes each). + right_raw = ((payload[1] & 0x03) << 8) | payload[0] + left_raw = ((payload[5] & 0x03) << 8) | payload[4] if left_raw < 8 and right_raw < 8: return 0.0, 0.0 left = min(max(left_raw / 1023.0, 0.0), 1.0) @@ -278,19 +282,20 @@ def decode_rumble(payload: bytes) -> Tuple[float, float]: return left, right -def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> None: +def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> float: left_norm, right_norm = decode_rumble(payload) max_norm = max(left_norm, right_norm) # Treat small rumble as "off" to avoid idle buzz. - if max_norm < 0.40: + if max_norm < RUMBLE_MIN_ACTIVE: sdl2.SDL_GameControllerRumble(controller, 0, 0, 0) - return + return 0.0 # Attenuate to feel closer to a real controller; cap at ~25% strength. - scale = 0.40 - left = int(min(1.0, left_norm * scale) * 0xFFFF) - right = int(min(1.0, right_norm * scale) * 0xFFFF) + scale = 0.60 + low = int(min(1.0, left_norm * scale) * 0xFFFF) # SDL: low_frequency_rumble + high = int(min(1.0, right_norm * scale) * 0xFFFF) # SDL: high_frequency_rumble duration = 10 - sdl2.SDL_GameControllerRumble(controller, left, right, duration) + sdl2.SDL_GameControllerRumble(controller, low, high, duration) + return max_norm @dataclass @@ -298,13 +303,17 @@ class ControllerContext: controller: sdl2.SDL_GameController instance_id: int controller_index: int - port: str + port: Optional[str] uart: Optional[PicoUART] report: SwitchReport = field(default_factory=SwitchReport) dpad: Dict[str, bool] = field(default_factory=lambda: {"up": False, "down": False, "left": False, "right": False}) last_trigger_state: Dict[str, bool] = field(default_factory=lambda: {"left": False, "right": False}) last_send: float = 0.0 last_reopen_attempt: float = 0.0 + last_rumble: float = 0.0 + last_rumble_change: float = 0.0 + last_rumble_energy: float = 0.0 + rumble_active: bool = False def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: @@ -407,6 +416,11 @@ def main() -> None: controller_count = sdl2.SDL_NumJoysticks() if controller_count < 0: parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") + auto_pairing_enabled = not args.map and not args.interactive + auto_discover_ports = auto_pairing_enabled and not args.ports + available_ports: List[str] = [] + auto_assigned_indices: set[int] = set() + include_non_usb = args.all_ports or False for index in range(controller_count): if sdl2.SDL_IsGameController(index): name = sdl2.SDL_GameControllerNameForIndex(index) @@ -423,70 +437,65 @@ def main() -> None: if args.interactive: if not controller_indices: parser.error("No controllers detected for interactive pairing.") - discovered = discover_ports(include_non_usb=args.all_ports or False) + discovered = discover_ports(include_non_usb=include_non_usb) if not discovered: parser.error("No UART devices found for interactive pairing.") mappings = interactive_pairing(console, controller_names, discovered) if not mappings: parser.error("No controller-to-UART mappings were selected.") - elif not mappings: + elif auto_pairing_enabled: if args.ports: - validated = list(args.ports) - if not controller_indices: - parser.error("No controllers available to pair.") - pair_count = min(len(controller_indices), len(validated)) - if pair_count == 0: - parser.error("No UART ports specified.") - if len(validated) < len(controller_indices): - console.print( - f"[yellow]Warning: only {len(validated)} UART(s) provided; pairing first {pair_count} controllers.[/yellow]" - ) - mappings = list((controller_indices[i], validated[i]) for i in range(pair_count)) - console.print("[green]Paired controllers to specified UART ports:[/green]") - for idx, port in mappings: - console.print(f" Controller {idx} -> {port}") + available_ports.extend(list(args.ports)) + console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") else: - discovered = discover_ports(include_non_usb=args.all_ports or False) - if not discovered: - parser.error("No UART devices detected automatically.") - pair_count = min(len(controller_indices), len(discovered)) - if pair_count == 0: - parser.error("No controllers detected to pair.") - if len(discovered) < len(controller_indices): - console.print( - f"[yellow]Warning: detected {len(discovered)} UART(s) but {len(controller_indices)} controller(s); pairing first {pair_count}.[/yellow]" - ) - mappings = list((controller_indices[i], discovered[i]["device"]) for i in range(pair_count)) - console.print("[green]Auto-detected UARTs:[/green]") - for info in discovered: - console.print(f" {info['device']} ({info['description']})") - console.print("[green]Paired controllers to detected UARTs:[/green]") - for idx, port in mappings: - console.print(f" Controller {idx} -> {port}") + discovered = discover_ports(include_non_usb=include_non_usb) + if discovered: + available_ports.extend(info["device"] for info in discovered) + console.print("[green]Auto-detected UARTs:[/green]") + for info in discovered: + console.print(f" {info['device']} ({info['description']})") + else: + console.print("[yellow]No UART devices detected yet; waiting for hotplug...[/yellow]") for index, port in mappings: mapping_by_index[index] = port - # Open currently connected controllers that match the mapping. - for index, port in mappings: + def assign_port_for_index(idx: int) -> Optional[str]: + if idx in mapping_by_index: + return mapping_by_index[idx] + if not auto_pairing_enabled: + return None + if not available_ports: + return None + port_choice = available_ports.pop(0) + mapping_by_index[idx] = port_choice + auto_assigned_indices.add(idx) + console.print(f"[green]Auto-paired controller {idx} to {port_choice}[/green]") + return port_choice + + # Open currently connected controllers that we can pair. + for index in controller_indices: if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): - # Try to turn non-GameController into a controller via hint reload. - if index < sdl2.SDL_NumJoysticks(): - name = sdl2.SDL_JoystickNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) - console.print(f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") + name = sdl2.SDL_JoystickNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print(f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") + continue + port = assign_port_for_index(index) + if port is None and not auto_pairing_enabled: continue try: controller, instance_id = open_controller(index) except Exception as exc: console.print(f"[red]Failed to open controller {index}: {exc}[/red]") continue - uart = try_open_uart(port, args.baud) + uart = try_open_uart(port, args.baud) if port else None if uart: uarts.append(uart) console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") - else: + elif port: console.print(f"[yellow]Controller {index} ({instance_id}) waiting for UART {port}[/yellow]") + else: + console.print(f"[yellow]Controller {index} ({instance_id}) connected; waiting for an available UART[/yellow]") ctx = ControllerContext( controller=controller, instance_id=instance_id, @@ -496,10 +505,51 @@ def main() -> None: ) contexts[instance_id] = ctx - if not contexts and not mapping_by_index: - parser.error("No controllers opened. Check --map/--ports/--interactive values.") + if not contexts: + console.print("[yellow]No controllers opened; waiting for hotplug events...[/yellow]") + + def ports_in_use() -> set: + used = set(mapping_by_index.values()) + used.update(ctx.port for ctx in contexts.values() if ctx.port) + return used + + def discover_new_ports() -> None: + if not auto_discover_ports: + return + discovered = discover_ports(include_non_usb=include_non_usb) + in_use = ports_in_use() + for info in discovered: + path = info["device"] + if path in in_use or path in available_ports: + continue + available_ports.append(path) + console.print(f"[green]Discovered UART {path} ({info['description']}); available for pairing.[/green]") + + def pair_waiting_contexts() -> None: + for ctx in list(contexts.values()): + if ctx.port is not None: + continue + port_choice = assign_port_for_index(ctx.controller_index) + if port_choice is None: + continue + ctx.port = port_choice + uart = try_open_uart(port_choice, args.baud) + ctx.last_reopen_attempt = time.monotonic() + if uart: + uarts.append(uart) + ctx.uart = uart + console.print( + f"[green]Controller {ctx.controller_index} ({ctx.instance_id}) paired to {port_choice}[/green]" + ) + else: + ctx.uart = None + console.print( + f"[yellow]Controller {ctx.controller_index} ({ctx.instance_id}) waiting for UART {port_choice}[/yellow]" + ) event = sdl2.SDL_Event() + port_scan_interval = 2.0 + last_port_scan = time.monotonic() running = True while running: while sdl2.SDL_PollEvent(event): @@ -553,24 +603,31 @@ def main() -> None: ctx.report.hat = dpad_to_hat(ctx.dpad) elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: idx = event.cdevice.which - port = mapping_by_index.get(idx) - if port is None: + if any(c.controller_index == idx for c in contexts.values()): continue - # Avoid duplicate opens for already connected instance IDs. - already = any(c.controller_index == idx for c in contexts.values()) - if already: + port = assign_port_for_index(idx) + if port is None and not auto_pairing_enabled: + continue + if idx >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(idx): + name = sdl2.SDL_JoystickNameForIndex(idx) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print( + f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" + ) continue try: controller, instance_id = open_controller(idx) except Exception as exc: console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") continue - uart = try_open_uart(port, args.baud) + uart = try_open_uart(port, args.baud) if port else None if uart: uarts.append(uart) console.print(f"[green]Controller {idx} ({instance_id}) paired to {port}[/green]") - else: + elif port: console.print(f"[yellow]Controller {idx} ({instance_id}) waiting for UART {port}[/yellow]") + else: + console.print(f"[yellow]Controller {idx} ({instance_id}) connected; waiting for an available UART[/yellow]") ctx = ControllerContext( controller=controller, instance_id=instance_id, @@ -589,12 +646,24 @@ def main() -> None: ctx = contexts.pop(instance_id, None) if ctx: console.print(f"[yellow]Controller {instance_id} removed[/yellow]") + if ctx.controller_index in auto_assigned_indices: + freed = mapping_by_index.pop(ctx.controller_index, None) + auto_assigned_indices.discard(ctx.controller_index) + if freed and freed not in available_ports: + available_ports.append(freed) + console.print(f"[cyan]Released UART {freed} back to pool[/cyan]") sdl2.SDL_GameControllerClose(ctx.controller) now = time.monotonic() + if now - last_port_scan > port_scan_interval: + discover_new_ports() + last_port_scan = now + pair_waiting_contexts() + else: + pair_waiting_contexts() for ctx in list(contexts.values()): # Reconnect UART if needed. - if ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: + if ctx.port and ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: ctx.last_reopen_attempt = now uart = try_open_uart(ctx.port, args.baud) if uart: @@ -610,14 +679,31 @@ def main() -> None: # Poll rumble quickly while we have the port. payload = ctx.uart.read_rumble_payload() if payload: - apply_rumble(ctx.controller, payload) + energy = apply_rumble(ctx.controller, payload) + ctx.rumble_active = energy >= RUMBLE_MIN_ACTIVE + if ctx.rumble_active and energy != ctx.last_rumble_energy: + ctx.last_rumble_change = now + ctx.last_rumble_energy = energy + ctx.last_rumble = now + elif ctx.rumble_active and (now - ctx.last_rumble) > RUMBLE_IDLE_TIMEOUT: + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 + elif ctx.rumble_active and (now - ctx.last_rumble_change) > RUMBLE_STUCK_TIMEOUT: + # Guard against a stream of tiny-but-nonzero rumble packets that never decay. + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 except SerialException as exc: console.print(f"[yellow]UART {ctx.port} disconnected: {exc}[/yellow]") try: ctx.uart.close() except Exception: pass + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) ctx.uart = None + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 ctx.last_reopen_attempt = now except Exception as exc: console.print(f"[red]UART error on {ctx.port}: {exc}[/red]") From fd9f86a182c40db31dc8a5611efd4fe6733af67a Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 21 Nov 2025 15:49:39 -0700 Subject: [PATCH 025/134] Add filtering --- controller_uart_bridge.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 84152e7..c50486d 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -153,13 +153,24 @@ def is_usb_serial(path: str) -> bool: return True +def is_usb_serial_port(port: list_ports.ListPortInfo) -> bool: + """Heuristic: prefer ports with USB VID/PID; fall back to path hints.""" + if getattr(port, "vid", None) is not None or getattr(port, "pid", None) is not None: + return True + path = port.device or "" + manufacturer = (getattr(port, "manufacturer", "") or "").upper() + if "USB" in manufacturer: + return True + return is_usb_serial(path) + + def discover_ports(include_non_usb: bool = False) -> List[Dict[str, str]]: results: List[Dict[str, str]] = [] for port in list_ports.comports(): path = port.device or "" if not path: continue - if not include_non_usb and not is_usb_serial(path): + if not include_non_usb and not is_usb_serial_port(port): continue results.append( { From b900cde25b2dbdaf735586d746fc326edb6bb29e Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 21 Nov 2025 15:49:39 -0700 Subject: [PATCH 026/134] Add filtering --- controller_uart_bridge.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 84152e7..c50486d 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -153,13 +153,24 @@ def is_usb_serial(path: str) -> bool: return True +def is_usb_serial_port(port: list_ports.ListPortInfo) -> bool: + """Heuristic: prefer ports with USB VID/PID; fall back to path hints.""" + if getattr(port, "vid", None) is not None or getattr(port, "pid", None) is not None: + return True + path = port.device or "" + manufacturer = (getattr(port, "manufacturer", "") or "").upper() + if "USB" in manufacturer: + return True + return is_usb_serial(path) + + def discover_ports(include_non_usb: bool = False) -> List[Dict[str, str]]: results: List[Dict[str, str]] = [] for port in list_ports.comports(): path = port.device or "" if not path: continue - if not include_non_usb and not is_usb_serial(path): + if not include_non_usb and not is_usb_serial_port(port): continue results.append( { From 92abe29340d61378d45b5bcb7b49d6a2e34c69ee Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 21 Nov 2025 15:59:37 -0700 Subject: [PATCH 027/134] Add include filter --- controller_uart_bridge.py | 45 +++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index c50486d..730c083 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -164,7 +164,13 @@ def is_usb_serial_port(port: list_ports.ListPortInfo) -> bool: return is_usb_serial(path) -def discover_ports(include_non_usb: bool = False) -> List[Dict[str, str]]: +def discover_ports( + include_non_usb: bool = False, + ignore_descriptions: Optional[List[str]] = None, + include_descriptions: Optional[List[str]] = None, +) -> List[Dict[str, str]]: + ignored = [d.lower() for d in ignore_descriptions or []] + includes = [d.lower() for d in include_descriptions or []] results: List[Dict[str, str]] = [] for port in list_ports.comports(): path = port.device or "" @@ -172,6 +178,11 @@ def discover_ports(include_non_usb: bool = False) -> List[Dict[str, str]]: continue if not include_non_usb and not is_usb_serial_port(port): continue + desc_lower = (port.description or "").lower() + if includes and not any(keep in desc_lower for keep in includes): + continue + if any(skip in desc_lower for skip in ignored): + continue results.append( { "device": path, @@ -383,6 +394,18 @@ def build_arg_parser() -> argparse.ArgumentParser: default=UART_BAUD, help=f"UART baud rate (default {UART_BAUD}; must match switch-pico firmware)", ) + parser.add_argument( + "--ignore-port-desc", + action="append", + default=[], + help="Substring filter to exclude serial ports by description (case-insensitive). Repeatable.", + ) + parser.add_argument( + "--include-port-desc", + action="append", + default=[], + help="Only include serial ports whose description contains this substring (case-insensitive). Repeatable.", + ) parser.add_argument( "--sdl-mapping", action="append", @@ -432,6 +455,8 @@ def main() -> None: available_ports: List[str] = [] auto_assigned_indices: set[int] = set() include_non_usb = args.all_ports or False + ignore_port_desc = [d.lower() for d in args.ignore_port_desc] + include_port_desc = [d.lower() for d in args.include_port_desc] for index in range(controller_count): if sdl2.SDL_IsGameController(index): name = sdl2.SDL_GameControllerNameForIndex(index) @@ -448,7 +473,11 @@ def main() -> None: if args.interactive: if not controller_indices: parser.error("No controllers detected for interactive pairing.") - discovered = discover_ports(include_non_usb=include_non_usb) + discovered = discover_ports( + include_non_usb=include_non_usb, + ignore_descriptions=ignore_port_desc, + include_descriptions=include_port_desc, + ) if not discovered: parser.error("No UART devices found for interactive pairing.") mappings = interactive_pairing(console, controller_names, discovered) @@ -459,7 +488,11 @@ def main() -> None: available_ports.extend(list(args.ports)) console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") else: - discovered = discover_ports(include_non_usb=include_non_usb) + discovered = discover_ports( + include_non_usb=include_non_usb, + ignore_descriptions=ignore_port_desc, + include_descriptions=include_port_desc, + ) if discovered: available_ports.extend(info["device"] for info in discovered) console.print("[green]Auto-detected UARTs:[/green]") @@ -527,7 +560,11 @@ def main() -> None: def discover_new_ports() -> None: if not auto_discover_ports: return - discovered = discover_ports(include_non_usb=include_non_usb) + discovered = discover_ports( + include_non_usb=include_non_usb, + ignore_descriptions=ignore_port_desc, + include_descriptions=include_port_desc, + ) in_use = ports_in_use() for info in discovered: path = info["device"] From 520eecbab4654a1ed5b9e8d1f13c3d95d27a4388 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 21 Nov 2025 15:59:37 -0700 Subject: [PATCH 028/134] Add include filter --- controller_uart_bridge.py | 45 +++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index c50486d..730c083 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -164,7 +164,13 @@ def is_usb_serial_port(port: list_ports.ListPortInfo) -> bool: return is_usb_serial(path) -def discover_ports(include_non_usb: bool = False) -> List[Dict[str, str]]: +def discover_ports( + include_non_usb: bool = False, + ignore_descriptions: Optional[List[str]] = None, + include_descriptions: Optional[List[str]] = None, +) -> List[Dict[str, str]]: + ignored = [d.lower() for d in ignore_descriptions or []] + includes = [d.lower() for d in include_descriptions or []] results: List[Dict[str, str]] = [] for port in list_ports.comports(): path = port.device or "" @@ -172,6 +178,11 @@ def discover_ports(include_non_usb: bool = False) -> List[Dict[str, str]]: continue if not include_non_usb and not is_usb_serial_port(port): continue + desc_lower = (port.description or "").lower() + if includes and not any(keep in desc_lower for keep in includes): + continue + if any(skip in desc_lower for skip in ignored): + continue results.append( { "device": path, @@ -383,6 +394,18 @@ def build_arg_parser() -> argparse.ArgumentParser: default=UART_BAUD, help=f"UART baud rate (default {UART_BAUD}; must match switch-pico firmware)", ) + parser.add_argument( + "--ignore-port-desc", + action="append", + default=[], + help="Substring filter to exclude serial ports by description (case-insensitive). Repeatable.", + ) + parser.add_argument( + "--include-port-desc", + action="append", + default=[], + help="Only include serial ports whose description contains this substring (case-insensitive). Repeatable.", + ) parser.add_argument( "--sdl-mapping", action="append", @@ -432,6 +455,8 @@ def main() -> None: available_ports: List[str] = [] auto_assigned_indices: set[int] = set() include_non_usb = args.all_ports or False + ignore_port_desc = [d.lower() for d in args.ignore_port_desc] + include_port_desc = [d.lower() for d in args.include_port_desc] for index in range(controller_count): if sdl2.SDL_IsGameController(index): name = sdl2.SDL_GameControllerNameForIndex(index) @@ -448,7 +473,11 @@ def main() -> None: if args.interactive: if not controller_indices: parser.error("No controllers detected for interactive pairing.") - discovered = discover_ports(include_non_usb=include_non_usb) + discovered = discover_ports( + include_non_usb=include_non_usb, + ignore_descriptions=ignore_port_desc, + include_descriptions=include_port_desc, + ) if not discovered: parser.error("No UART devices found for interactive pairing.") mappings = interactive_pairing(console, controller_names, discovered) @@ -459,7 +488,11 @@ def main() -> None: available_ports.extend(list(args.ports)) console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") else: - discovered = discover_ports(include_non_usb=include_non_usb) + discovered = discover_ports( + include_non_usb=include_non_usb, + ignore_descriptions=ignore_port_desc, + include_descriptions=include_port_desc, + ) if discovered: available_ports.extend(info["device"] for info in discovered) console.print("[green]Auto-detected UARTs:[/green]") @@ -527,7 +560,11 @@ def main() -> None: def discover_new_ports() -> None: if not auto_discover_ports: return - discovered = discover_ports(include_non_usb=include_non_usb) + discovered = discover_ports( + include_non_usb=include_non_usb, + ignore_descriptions=ignore_port_desc, + include_descriptions=include_port_desc, + ) in_use = ports_in_use() for info in discovered: path = info["device"] From bd21dc5ea69d37102bc89255f6ef5c32b23476b6 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 22 Nov 2025 08:22:25 -0700 Subject: [PATCH 029/134] Uart opening warning --- controller_uart_bridge.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 730c083..5ca5045 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -354,6 +354,14 @@ def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: return None +def open_uart_or_warn(port: str, baud: int, console: Console) -> Optional[PicoUART]: + try: + return PicoUART(port, baud) + except Exception as exc: + console.print(f"[yellow]Failed to open UART {port}: {exc}[/yellow]") + return None + + def start_rumble_listener(ctx: ControllerContext) -> threading.Thread: # No-op placeholder (rumble is polled in the main loop for hotplug safety). return None @@ -532,7 +540,7 @@ def main() -> None: except Exception as exc: console.print(f"[red]Failed to open controller {index}: {exc}[/red]") continue - uart = try_open_uart(port, args.baud) if port else None + uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") @@ -581,7 +589,7 @@ def main() -> None: if port_choice is None: continue ctx.port = port_choice - uart = try_open_uart(port_choice, args.baud) + uart = open_uart_or_warn(port_choice, args.baud, console) ctx.last_reopen_attempt = time.monotonic() if uart: uarts.append(uart) @@ -668,7 +676,7 @@ def main() -> None: except Exception as exc: console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") continue - uart = try_open_uart(port, args.baud) if port else None + uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) console.print(f"[green]Controller {idx} ({instance_id}) paired to {port}[/green]") @@ -713,7 +721,7 @@ def main() -> None: # Reconnect UART if needed. if ctx.port and ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: ctx.last_reopen_attempt = now - uart = try_open_uart(ctx.port, args.baud) + uart = open_uart_or_warn(ctx.port, args.baud, console) if uart: uarts.append(uart) console.print(f"[green]Reconnected UART {ctx.port} for controller {ctx.controller_index}[/green]") From d63a0c78b232656d0b6617d844414ee34822729e Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 22 Nov 2025 08:22:25 -0700 Subject: [PATCH 030/134] Uart opening warning --- controller_uart_bridge.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 730c083..5ca5045 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -354,6 +354,14 @@ def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: return None +def open_uart_or_warn(port: str, baud: int, console: Console) -> Optional[PicoUART]: + try: + return PicoUART(port, baud) + except Exception as exc: + console.print(f"[yellow]Failed to open UART {port}: {exc}[/yellow]") + return None + + def start_rumble_listener(ctx: ControllerContext) -> threading.Thread: # No-op placeholder (rumble is polled in the main loop for hotplug safety). return None @@ -532,7 +540,7 @@ def main() -> None: except Exception as exc: console.print(f"[red]Failed to open controller {index}: {exc}[/red]") continue - uart = try_open_uart(port, args.baud) if port else None + uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") @@ -581,7 +589,7 @@ def main() -> None: if port_choice is None: continue ctx.port = port_choice - uart = try_open_uart(port_choice, args.baud) + uart = open_uart_or_warn(port_choice, args.baud, console) ctx.last_reopen_attempt = time.monotonic() if uart: uarts.append(uart) @@ -668,7 +676,7 @@ def main() -> None: except Exception as exc: console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") continue - uart = try_open_uart(port, args.baud) if port else None + uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) console.print(f"[green]Controller {idx} ({instance_id}) paired to {port}[/green]") @@ -713,7 +721,7 @@ def main() -> None: # Reconnect UART if needed. if ctx.port and ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: ctx.last_reopen_attempt = now - uart = try_open_uart(ctx.port, args.baud) + uart = open_uart_or_warn(ctx.port, args.baud, console) if uart: uarts.append(uart) console.print(f"[green]Reconnected UART {ctx.port} for controller {ctx.controller_index}[/green]") From 3a4ca98fa91131e818acd4cc8a72d17b483ba2a1 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 22 Nov 2025 09:24:13 -0700 Subject: [PATCH 031/134] Add hints for pro controller --- controller_uart_bridge.py | 14 +++++++++++++- switch-pico.cpp | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 5ca5045..99a3dbf 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -36,7 +36,7 @@ from rich.table import Table UART_HEADER = 0xAA RUMBLE_HEADER = 0xBB RUMBLE_TYPE_RUMBLE = 0x01 -UART_BAUD = 900000 +UART_BAUD = 921600 RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this RUMBLE_MIN_ACTIVE = 0.50 # below this, rumble is treated as off/noise @@ -96,6 +96,14 @@ def trigger_to_button(value: int, threshold: int) -> bool: return value >= threshold +def set_hint(name: str, value: str) -> None: + """Set an SDL hint safely even if the constant is missing in PySDL2.""" + try: + sdl2.SDL_SetHint(name.encode(), value.encode()) + except Exception: + pass + + BUTTON_MAP = { # Direct mapping to the Switch PRO mask definitions in switch_pro_descriptors.h sdl2.SDL_CONTROLLER_BUTTON_A: SwitchButton.A, @@ -446,6 +454,10 @@ def main() -> None: console.print(f"[red]Failed to load SDL mapping {mapping_path}: {exc}[/red]") sdl2.SDL_SetHint(sdl2.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, b"1") + set_hint("SDL_JOYSTICK_HIDAPI", "1") + set_hint("SDL_JOYSTICK_HIDAPI_SWITCH", "1") + # Use controller button labels so Nintendo layouts (ABXY) map correctly on Linux. + set_hint("SDL_GAMECONTROLLER_USE_BUTTON_LABELS", "1") if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_JOYSTICK) != 0: parser.error(f"SDL init failed: {sdl2.SDL_GetError().decode(errors='ignore')}") contexts: Dict[int, ControllerContext] = {} diff --git a/switch-pico.cpp b/switch-pico.cpp index 881f4de..646bbe0 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -14,7 +14,7 @@ // UART1 is reserved for external input frames from the host PC. #define UART_ID uart1 -#define BAUD_RATE 900000 +#define BAUD_RATE 921600 #define UART_TX_PIN 4 #define UART_RX_PIN 5 #define UART_RUMBLE_HEADER 0xBB From 282832ed06306233f7a8866834b78c8187c2b14b Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 22 Nov 2025 09:24:13 -0700 Subject: [PATCH 032/134] Add hints for pro controller --- controller_uart_bridge.py | 14 +++++++++++++- switch-pico.cpp | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 5ca5045..99a3dbf 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -36,7 +36,7 @@ from rich.table import Table UART_HEADER = 0xAA RUMBLE_HEADER = 0xBB RUMBLE_TYPE_RUMBLE = 0x01 -UART_BAUD = 900000 +UART_BAUD = 921600 RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this RUMBLE_MIN_ACTIVE = 0.50 # below this, rumble is treated as off/noise @@ -96,6 +96,14 @@ def trigger_to_button(value: int, threshold: int) -> bool: return value >= threshold +def set_hint(name: str, value: str) -> None: + """Set an SDL hint safely even if the constant is missing in PySDL2.""" + try: + sdl2.SDL_SetHint(name.encode(), value.encode()) + except Exception: + pass + + BUTTON_MAP = { # Direct mapping to the Switch PRO mask definitions in switch_pro_descriptors.h sdl2.SDL_CONTROLLER_BUTTON_A: SwitchButton.A, @@ -446,6 +454,10 @@ def main() -> None: console.print(f"[red]Failed to load SDL mapping {mapping_path}: {exc}[/red]") sdl2.SDL_SetHint(sdl2.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, b"1") + set_hint("SDL_JOYSTICK_HIDAPI", "1") + set_hint("SDL_JOYSTICK_HIDAPI_SWITCH", "1") + # Use controller button labels so Nintendo layouts (ABXY) map correctly on Linux. + set_hint("SDL_GAMECONTROLLER_USE_BUTTON_LABELS", "1") if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_JOYSTICK) != 0: parser.error(f"SDL init failed: {sdl2.SDL_GetError().decode(errors='ignore')}") contexts: Dict[int, ControllerContext] = {} diff --git a/switch-pico.cpp b/switch-pico.cpp index 881f4de..646bbe0 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -14,7 +14,7 @@ // UART1 is reserved for external input frames from the host PC. #define UART_ID uart1 -#define BAUD_RATE 900000 +#define BAUD_RATE 921600 #define UART_TX_PIN 4 #define UART_RX_PIN 5 #define UART_RUMBLE_HEADER 0xBB From 702b96eaa5c5c42a866457f0ea9dc40d0aedbc34 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 22 Nov 2025 09:27:40 -0700 Subject: [PATCH 033/134] Add abxy swapping --- controller_uart_bridge.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 99a3dbf..e5dbb8c 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -422,6 +422,18 @@ def build_arg_parser() -> argparse.ArgumentParser: default=[], help="Only include serial ports whose description contains this substring (case-insensitive). Repeatable.", ) + parser.add_argument( + "--swap-abxy", + action="store_true", + help="Swap AB/XY mapping (useful if Linux reports Switch controllers as Xbox layout).", + ) + parser.add_argument( + "--swap-abxy-index", + action="append", + type=int, + default=[], + help="Swap AB/XY mapping for specific controller indices (repeatable).", + ) parser.add_argument( "--sdl-mapping", action="append", @@ -444,6 +456,13 @@ def main() -> None: if default_mapping.exists(): mappings_to_load.append(str(default_mapping)) mappings_to_load.extend(args.sdl_mapping) + button_map_default = dict(BUTTON_MAP) + button_map_swapped = dict(BUTTON_MAP) + button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_A] = SwitchButton.B + button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_B] = SwitchButton.A + button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_X] = SwitchButton.Y + button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_Y] = SwitchButton.X + swap_abxy_indices = set(idx for idx in args.swap_abxy_index if idx is not None and idx >= 0) for mapping_path in mappings_to_load: try: loaded = sdl2.SDL_GameControllerAddMappingsFromFile(mapping_path.encode()) @@ -658,10 +677,15 @@ def main() -> None: ctx = contexts.get(event.cbutton.which) if not ctx: continue + current_button_map = ( + button_map_swapped + if (args.swap_abxy or ctx.controller_index in swap_abxy_indices) + else button_map_default + ) button = event.cbutton.button pressed = event.type == sdl2.SDL_CONTROLLERBUTTONDOWN - if button in BUTTON_MAP: - bit = BUTTON_MAP[button] + if button in current_button_map: + bit = current_button_map[button] if pressed: ctx.report.buttons |= bit else: From c69db9bc28cf5b3e8b7050bc779bd8cb56fe9619 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 22 Nov 2025 09:27:40 -0700 Subject: [PATCH 034/134] Add abxy swapping --- controller_uart_bridge.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 99a3dbf..e5dbb8c 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -422,6 +422,18 @@ def build_arg_parser() -> argparse.ArgumentParser: default=[], help="Only include serial ports whose description contains this substring (case-insensitive). Repeatable.", ) + parser.add_argument( + "--swap-abxy", + action="store_true", + help="Swap AB/XY mapping (useful if Linux reports Switch controllers as Xbox layout).", + ) + parser.add_argument( + "--swap-abxy-index", + action="append", + type=int, + default=[], + help="Swap AB/XY mapping for specific controller indices (repeatable).", + ) parser.add_argument( "--sdl-mapping", action="append", @@ -444,6 +456,13 @@ def main() -> None: if default_mapping.exists(): mappings_to_load.append(str(default_mapping)) mappings_to_load.extend(args.sdl_mapping) + button_map_default = dict(BUTTON_MAP) + button_map_swapped = dict(BUTTON_MAP) + button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_A] = SwitchButton.B + button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_B] = SwitchButton.A + button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_X] = SwitchButton.Y + button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_Y] = SwitchButton.X + swap_abxy_indices = set(idx for idx in args.swap_abxy_index if idx is not None and idx >= 0) for mapping_path in mappings_to_load: try: loaded = sdl2.SDL_GameControllerAddMappingsFromFile(mapping_path.encode()) @@ -658,10 +677,15 @@ def main() -> None: ctx = contexts.get(event.cbutton.which) if not ctx: continue + current_button_map = ( + button_map_swapped + if (args.swap_abxy or ctx.controller_index in swap_abxy_indices) + else button_map_default + ) button = event.cbutton.button pressed = event.type == sdl2.SDL_CONTROLLERBUTTONDOWN - if button in BUTTON_MAP: - bit = BUTTON_MAP[button] + if button in current_button_map: + bit = current_button_map[button] if pressed: ctx.report.buttons |= bit else: From 900c5b89f0fa323e11e9c2f0ba0279881dd820a5 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 22 Nov 2025 10:12:30 -0700 Subject: [PATCH 035/134] Add include for controllers --- controller_uart_bridge.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index e5dbb8c..7f59595 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -422,6 +422,12 @@ def build_arg_parser() -> argparse.ArgumentParser: default=[], help="Only include serial ports whose description contains this substring (case-insensitive). Repeatable.", ) + parser.add_argument( + "--include-controller-name", + action="append", + default=[], + help="Only open controllers whose name contains this substring (case-insensitive). Repeatable.", + ) parser.add_argument( "--swap-abxy", action="store_true", @@ -496,16 +502,23 @@ def main() -> None: include_non_usb = args.all_ports or False ignore_port_desc = [d.lower() for d in args.ignore_port_desc] include_port_desc = [d.lower() for d in args.include_port_desc] + include_controller_name = [n.lower() for n in args.include_controller_name] for index in range(controller_count): if sdl2.SDL_IsGameController(index): name = sdl2.SDL_GameControllerNameForIndex(index) name_str = name.decode() if isinstance(name, bytes) else str(name) + if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): + console.print(f"[yellow]Skipping controller {index} ({name_str}) due to name filter[/yellow]") + continue console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") controller_indices.append(index) controller_names[index] = name_str else: name = sdl2.SDL_JoystickNameForIndex(index) name_str = name.decode() if isinstance(name, bytes) else str(name) + if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): + console.print(f"[yellow]Skipping joystick {index} ({name_str}) due to name filter[/yellow]") + continue console.print(f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]") mappings = list(args.map) From a700640f9d00818142155ecf4bc82a7de8ed1520 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 22 Nov 2025 10:12:30 -0700 Subject: [PATCH 036/134] Add include for controllers --- controller_uart_bridge.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index e5dbb8c..7f59595 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -422,6 +422,12 @@ def build_arg_parser() -> argparse.ArgumentParser: default=[], help="Only include serial ports whose description contains this substring (case-insensitive). Repeatable.", ) + parser.add_argument( + "--include-controller-name", + action="append", + default=[], + help="Only open controllers whose name contains this substring (case-insensitive). Repeatable.", + ) parser.add_argument( "--swap-abxy", action="store_true", @@ -496,16 +502,23 @@ def main() -> None: include_non_usb = args.all_ports or False ignore_port_desc = [d.lower() for d in args.ignore_port_desc] include_port_desc = [d.lower() for d in args.include_port_desc] + include_controller_name = [n.lower() for n in args.include_controller_name] for index in range(controller_count): if sdl2.SDL_IsGameController(index): name = sdl2.SDL_GameControllerNameForIndex(index) name_str = name.decode() if isinstance(name, bytes) else str(name) + if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): + console.print(f"[yellow]Skipping controller {index} ({name_str}) due to name filter[/yellow]") + continue console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") controller_indices.append(index) controller_names[index] = name_str else: name = sdl2.SDL_JoystickNameForIndex(index) name_str = name.decode() if isinstance(name, bytes) else str(name) + if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): + console.print(f"[yellow]Skipping joystick {index} ({name_str}) due to name filter[/yellow]") + continue console.print(f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]") mappings = list(args.map) From f8f7cc4d083ff25bbf8e06b04e49dfd74568e551 Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Sat, 22 Nov 2025 11:08:24 -0700 Subject: [PATCH 037/134] Types --- controller_uart_bridge.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 7f59595..7c60667 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -26,6 +26,7 @@ from typing import Dict, List, Optional, Tuple import serial from serial import SerialException from serial.tools import list_ports +from serial.tools import list_ports_common import sdl2 import sdl2.ext @@ -161,7 +162,7 @@ def is_usb_serial(path: str) -> bool: return True -def is_usb_serial_port(port: list_ports.ListPortInfo) -> bool: +def is_usb_serial_port(port: list_ports_common.ListPortInfo) -> bool: """Heuristic: prefer ports with USB VID/PID; fall back to path hints.""" if getattr(port, "vid", None) is not None or getattr(port, "pid", None) is not None: return True @@ -259,6 +260,9 @@ class PicoUART: parity=serial.PARITY_NONE, timeout=0.0, write_timeout=0.0, + xonxoff=False, + rtscts=False, + dsrdtr=False ) self._buffer = bytearray() From d24cfad5dd02258119461def363bf7fc3a24e0cb Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Sat, 22 Nov 2025 11:08:24 -0700 Subject: [PATCH 038/134] Types --- controller_uart_bridge.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 7f59595..7c60667 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -26,6 +26,7 @@ from typing import Dict, List, Optional, Tuple import serial from serial import SerialException from serial.tools import list_ports +from serial.tools import list_ports_common import sdl2 import sdl2.ext @@ -161,7 +162,7 @@ def is_usb_serial(path: str) -> bool: return True -def is_usb_serial_port(port: list_ports.ListPortInfo) -> bool: +def is_usb_serial_port(port: list_ports_common.ListPortInfo) -> bool: """Heuristic: prefer ports with USB VID/PID; fall back to path hints.""" if getattr(port, "vid", None) is not None or getattr(port, "pid", None) is not None: return True @@ -259,6 +260,9 @@ class PicoUART: parity=serial.PARITY_NONE, timeout=0.0, write_timeout=0.0, + xonxoff=False, + rtscts=False, + dsrdtr=False ) self._buffer = bytearray() From 25212d870bd8d12cc099a40e974c2b81404d7e53 Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Sat, 22 Nov 2025 18:37:54 -0700 Subject: [PATCH 039/134] Make stuff work in windows --- controller_uart_bridge.py | 108 ++++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 28 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 7c60667..dd38821 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -41,6 +41,7 @@ UART_BAUD = 921600 RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this RUMBLE_MIN_ACTIVE = 0.50 # below this, rumble is treated as off/noise +RUMBLE_SCALE = 0.8 class SwitchButton: @@ -262,37 +263,56 @@ class PicoUART: write_timeout=0.0, xonxoff=False, rtscts=False, - dsrdtr=False + dsrdtr=False, ) self._buffer = bytearray() def send_report(self, report: SwitchReport) -> None: + # Non-blocking write; no flush to avoid sync stalls. self.serial.write(report.to_bytes()) - self.serial.flush() def read_rumble_payload(self) -> Optional[bytes]: - chunk = self.serial.read(64) # non-blocking (timeout=0) - if chunk: - self._buffer.extend(chunk) + """ + Drain all currently available UART bytes into an internal buffer, + then try to extract a single valid rumble frame. + + Frame format: + 0: 0xBB (RUMBLE_HEADER) + 1: type (0x01 for rumble) + 2-9: 8-byte rumble payload + 10: checksum (sum of first 10 bytes) & 0xFF + """ + # Read whatever is waiting in OS buffer + waiting = self.serial.in_waiting + if waiting: + self._buffer.extend(self.serial.read(waiting)) while True: - if RUMBLE_HEADER not in self._buffer: + if not self._buffer: + return None + + start = self._buffer.find(bytes([RUMBLE_HEADER])) + if start < 0: + # No header at all, drop garbage self._buffer.clear() return None - start = self._buffer.find(bytes([RUMBLE_HEADER])) + + # Not enough data for a full frame yet if len(self._buffer) - start < 11: - # Need more bytes. if start > 0: del self._buffer[:start] return None - frame = self._buffer[start : start + 11] + + frame = self._buffer[start:start + 11] checksum = sum(frame[:10]) & 0xFF + if frame[1] == RUMBLE_TYPE_RUMBLE and checksum == frame[10]: payload = bytes(frame[2:10]) - del self._buffer[: start + 11] + del self._buffer[:start + 11] return payload - # Bad frame, drop the header and continue. - del self._buffer[: start + 1] + + # Bad frame, drop this header and resync + del self._buffer[:start + 1] def close(self) -> None: self.serial.close() @@ -324,7 +344,7 @@ def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> float: sdl2.SDL_GameControllerRumble(controller, 0, 0, 0) return 0.0 # Attenuate to feel closer to a real controller; cap at ~25% strength. - scale = 0.60 + scale = RUMBLE_SCALE low = int(min(1.0, left_norm * scale) * 0xFFFF) # SDL: low_frequency_rumble high = int(min(1.0, right_norm * scale) * 0xFFFF) # SDL: high_frequency_rumble duration = 10 @@ -341,6 +361,7 @@ class ControllerContext: uart: Optional[PicoUART] report: SwitchReport = field(default_factory=SwitchReport) dpad: Dict[str, bool] = field(default_factory=lambda: {"up": False, "down": False, "left": False, "right": False}) + button_state: Dict[int, bool] = field(default_factory=dict) last_trigger_state: Dict[str, bool] = field(default_factory=lambda: {"left": False, "right": False}) last_send: float = 0.0 last_reopen_attempt: float = 0.0 @@ -374,11 +395,6 @@ def open_uart_or_warn(port: str, baud: int, console: Console) -> Optional[PicoUA return None -def start_rumble_listener(ctx: ControllerContext) -> threading.Thread: - # No-op placeholder (rumble is polled in the main loop for hotplug safety). - return None - - def build_arg_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Bridge SDL2 controllers to switch-pico UART (with rumble)") parser.add_argument( @@ -453,6 +469,32 @@ def build_arg_parser() -> argparse.ArgumentParser: return parser +def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, int]) -> None: + changed = False + for sdl_button, switch_bit in button_map.items(): + pressed = bool(sdl2.SDL_GameControllerGetButton(ctx.controller, sdl_button)) + previous = ctx.button_state.get(sdl_button) + if previous == pressed: + continue + ctx.button_state[sdl_button] = pressed + if pressed: + ctx.report.buttons |= switch_bit + else: + ctx.report.buttons &= ~switch_bit + changed = True + + dpad_changed = False + for sdl_button, name in DPAD_BUTTONS.items(): + pressed = bool(sdl2.SDL_GameControllerGetButton(ctx.controller, sdl_button)) + if ctx.dpad[name] == pressed: + continue + ctx.dpad[name] = pressed + dpad_changed = True + + if dpad_changed: + ctx.report.hat = dpad_to_hat(ctx.dpad) + + def main() -> None: parser = build_arg_parser() args = parser.parse_args() @@ -487,7 +529,7 @@ def main() -> None: set_hint("SDL_JOYSTICK_HIDAPI_SWITCH", "1") # Use controller button labels so Nintendo layouts (ABXY) map correctly on Linux. set_hint("SDL_GAMECONTROLLER_USE_BUTTON_LABELS", "1") - if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_JOYSTICK) != 0: + if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_JOYSTICK | sdl2.SDL_INIT_EVERYTHING) != 0: parser.error(f"SDL init failed: {sdl2.SDL_GetError().decode(errors='ignore')}") contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] @@ -707,6 +749,7 @@ def main() -> None: ctx.report.buttons |= bit else: ctx.report.buttons &= ~bit + ctx.button_state[button] = pressed elif button in DPAD_BUTTONS: ctx.dpad[DPAD_BUTTONS[button]] = pressed ctx.report.hat = dpad_to_hat(ctx.dpad) @@ -747,11 +790,6 @@ def main() -> None: contexts[instance_id] = ctx elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: instance_id = event.cdevice.which - # ctx = contexts.get(instance_id) - # if ctx and sdl2.SDL_GameControllerGetAttached(ctx.controller): - # # Spurious detach; ignore. - # console.print(f"[yellow]Ignoring spurious remove for controller {instance_id}[/yellow]") - # continue ctx = contexts.pop(instance_id, None) if ctx: console.print(f"[yellow]Controller {instance_id} removed[/yellow]") @@ -771,6 +809,12 @@ def main() -> None: else: pair_waiting_contexts() for ctx in list(contexts.values()): + current_button_map = ( + button_map_swapped + if (args.swap_abxy or ctx.controller_index in swap_abxy_indices) + else button_map_default + ) + poll_controller_buttons(ctx, current_button_map) # Reconnect UART if needed. if ctx.port and ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: ctx.last_reopen_attempt = now @@ -782,13 +826,21 @@ def main() -> None: if ctx.uart is None: continue try: + # Send controller state at configured frequency if now - ctx.last_send >= interval: ctx.uart.send_report(ctx.report) ctx.last_send = now - # Poll rumble quickly while we have the port. - payload = ctx.uart.read_rumble_payload() - if payload: - energy = apply_rumble(ctx.controller, payload) + + # Drain all pending rumble frames and apply only the latest one. + last_payload = None + while True: + p = ctx.uart.read_rumble_payload() + if not p: + break + last_payload = p + + if last_payload is not None: + energy = apply_rumble(ctx.controller, last_payload) ctx.rumble_active = energy >= RUMBLE_MIN_ACTIVE if ctx.rumble_active and energy != ctx.last_rumble_energy: ctx.last_rumble_change = now From bf4e6f75eae02809ccfcb626afe32c2fb2e04c7a Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Sat, 22 Nov 2025 18:37:54 -0700 Subject: [PATCH 040/134] Make stuff work in windows --- controller_uart_bridge.py | 108 ++++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 28 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 7c60667..dd38821 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -41,6 +41,7 @@ UART_BAUD = 921600 RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this RUMBLE_MIN_ACTIVE = 0.50 # below this, rumble is treated as off/noise +RUMBLE_SCALE = 0.8 class SwitchButton: @@ -262,37 +263,56 @@ class PicoUART: write_timeout=0.0, xonxoff=False, rtscts=False, - dsrdtr=False + dsrdtr=False, ) self._buffer = bytearray() def send_report(self, report: SwitchReport) -> None: + # Non-blocking write; no flush to avoid sync stalls. self.serial.write(report.to_bytes()) - self.serial.flush() def read_rumble_payload(self) -> Optional[bytes]: - chunk = self.serial.read(64) # non-blocking (timeout=0) - if chunk: - self._buffer.extend(chunk) + """ + Drain all currently available UART bytes into an internal buffer, + then try to extract a single valid rumble frame. + + Frame format: + 0: 0xBB (RUMBLE_HEADER) + 1: type (0x01 for rumble) + 2-9: 8-byte rumble payload + 10: checksum (sum of first 10 bytes) & 0xFF + """ + # Read whatever is waiting in OS buffer + waiting = self.serial.in_waiting + if waiting: + self._buffer.extend(self.serial.read(waiting)) while True: - if RUMBLE_HEADER not in self._buffer: + if not self._buffer: + return None + + start = self._buffer.find(bytes([RUMBLE_HEADER])) + if start < 0: + # No header at all, drop garbage self._buffer.clear() return None - start = self._buffer.find(bytes([RUMBLE_HEADER])) + + # Not enough data for a full frame yet if len(self._buffer) - start < 11: - # Need more bytes. if start > 0: del self._buffer[:start] return None - frame = self._buffer[start : start + 11] + + frame = self._buffer[start:start + 11] checksum = sum(frame[:10]) & 0xFF + if frame[1] == RUMBLE_TYPE_RUMBLE and checksum == frame[10]: payload = bytes(frame[2:10]) - del self._buffer[: start + 11] + del self._buffer[:start + 11] return payload - # Bad frame, drop the header and continue. - del self._buffer[: start + 1] + + # Bad frame, drop this header and resync + del self._buffer[:start + 1] def close(self) -> None: self.serial.close() @@ -324,7 +344,7 @@ def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> float: sdl2.SDL_GameControllerRumble(controller, 0, 0, 0) return 0.0 # Attenuate to feel closer to a real controller; cap at ~25% strength. - scale = 0.60 + scale = RUMBLE_SCALE low = int(min(1.0, left_norm * scale) * 0xFFFF) # SDL: low_frequency_rumble high = int(min(1.0, right_norm * scale) * 0xFFFF) # SDL: high_frequency_rumble duration = 10 @@ -341,6 +361,7 @@ class ControllerContext: uart: Optional[PicoUART] report: SwitchReport = field(default_factory=SwitchReport) dpad: Dict[str, bool] = field(default_factory=lambda: {"up": False, "down": False, "left": False, "right": False}) + button_state: Dict[int, bool] = field(default_factory=dict) last_trigger_state: Dict[str, bool] = field(default_factory=lambda: {"left": False, "right": False}) last_send: float = 0.0 last_reopen_attempt: float = 0.0 @@ -374,11 +395,6 @@ def open_uart_or_warn(port: str, baud: int, console: Console) -> Optional[PicoUA return None -def start_rumble_listener(ctx: ControllerContext) -> threading.Thread: - # No-op placeholder (rumble is polled in the main loop for hotplug safety). - return None - - def build_arg_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Bridge SDL2 controllers to switch-pico UART (with rumble)") parser.add_argument( @@ -453,6 +469,32 @@ def build_arg_parser() -> argparse.ArgumentParser: return parser +def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, int]) -> None: + changed = False + for sdl_button, switch_bit in button_map.items(): + pressed = bool(sdl2.SDL_GameControllerGetButton(ctx.controller, sdl_button)) + previous = ctx.button_state.get(sdl_button) + if previous == pressed: + continue + ctx.button_state[sdl_button] = pressed + if pressed: + ctx.report.buttons |= switch_bit + else: + ctx.report.buttons &= ~switch_bit + changed = True + + dpad_changed = False + for sdl_button, name in DPAD_BUTTONS.items(): + pressed = bool(sdl2.SDL_GameControllerGetButton(ctx.controller, sdl_button)) + if ctx.dpad[name] == pressed: + continue + ctx.dpad[name] = pressed + dpad_changed = True + + if dpad_changed: + ctx.report.hat = dpad_to_hat(ctx.dpad) + + def main() -> None: parser = build_arg_parser() args = parser.parse_args() @@ -487,7 +529,7 @@ def main() -> None: set_hint("SDL_JOYSTICK_HIDAPI_SWITCH", "1") # Use controller button labels so Nintendo layouts (ABXY) map correctly on Linux. set_hint("SDL_GAMECONTROLLER_USE_BUTTON_LABELS", "1") - if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_JOYSTICK) != 0: + if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_JOYSTICK | sdl2.SDL_INIT_EVERYTHING) != 0: parser.error(f"SDL init failed: {sdl2.SDL_GetError().decode(errors='ignore')}") contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] @@ -707,6 +749,7 @@ def main() -> None: ctx.report.buttons |= bit else: ctx.report.buttons &= ~bit + ctx.button_state[button] = pressed elif button in DPAD_BUTTONS: ctx.dpad[DPAD_BUTTONS[button]] = pressed ctx.report.hat = dpad_to_hat(ctx.dpad) @@ -747,11 +790,6 @@ def main() -> None: contexts[instance_id] = ctx elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: instance_id = event.cdevice.which - # ctx = contexts.get(instance_id) - # if ctx and sdl2.SDL_GameControllerGetAttached(ctx.controller): - # # Spurious detach; ignore. - # console.print(f"[yellow]Ignoring spurious remove for controller {instance_id}[/yellow]") - # continue ctx = contexts.pop(instance_id, None) if ctx: console.print(f"[yellow]Controller {instance_id} removed[/yellow]") @@ -771,6 +809,12 @@ def main() -> None: else: pair_waiting_contexts() for ctx in list(contexts.values()): + current_button_map = ( + button_map_swapped + if (args.swap_abxy or ctx.controller_index in swap_abxy_indices) + else button_map_default + ) + poll_controller_buttons(ctx, current_button_map) # Reconnect UART if needed. if ctx.port and ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: ctx.last_reopen_attempt = now @@ -782,13 +826,21 @@ def main() -> None: if ctx.uart is None: continue try: + # Send controller state at configured frequency if now - ctx.last_send >= interval: ctx.uart.send_report(ctx.report) ctx.last_send = now - # Poll rumble quickly while we have the port. - payload = ctx.uart.read_rumble_payload() - if payload: - energy = apply_rumble(ctx.controller, payload) + + # Drain all pending rumble frames and apply only the latest one. + last_payload = None + while True: + p = ctx.uart.read_rumble_payload() + if not p: + break + last_payload = p + + if last_payload is not None: + energy = apply_rumble(ctx.controller, last_payload) ctx.rumble_active = energy >= RUMBLE_MIN_ACTIVE if ctx.rumble_active and energy != ctx.last_rumble_energy: ctx.last_rumble_change = now From 963c19c5d6660bd217e7450893acc389f8521f12 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 23 Nov 2025 09:27:16 -0700 Subject: [PATCH 041/134] Refactor --- controller_uart_bridge.py | 813 ++++++++++++++++++++++---------------- 1 file changed, 471 insertions(+), 342 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index dd38821..81f77da 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -495,16 +495,32 @@ def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, int]) ctx.report.hat = dpad_to_hat(ctx.dpad) -def main() -> None: - parser = build_arg_parser() - args = parser.parse_args() - interval = 1.0 / max(args.frequency, 1.0) - deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) - trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) +@dataclass +class BridgeConfig: + interval: float + deadzone_raw: int + trigger_threshold: int + button_map_default: Dict[int, int] + button_map_swapped: Dict[int, int] + swap_abxy_indices: set[int] - # Load bundled mapping plus any user-supplied mapping files. + +@dataclass +class PairingState: + mapping_by_index: Dict[int, str] + available_ports: List[str] + auto_assigned_indices: set[int] = field(default_factory=set) + auto_pairing_enabled: bool = False + auto_discover_ports: bool = False + include_non_usb: bool = False + ignore_port_desc: List[str] = field(default_factory=list) + include_port_desc: List[str] = field(default_factory=list) + + +def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, int], Dict[int, int], set[int]]: + """Load SDL controller mappings and return button map variants.""" default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" - mappings_to_load = [] + mappings_to_load: List[str] = [] if default_mapping.exists(): mappings_to_load.append(str(default_mapping)) mappings_to_load.extend(args.sdl_mapping) @@ -514,16 +530,32 @@ def main() -> None: button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_B] = SwitchButton.A button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_X] = SwitchButton.Y button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_Y] = SwitchButton.X - swap_abxy_indices = set(idx for idx in args.swap_abxy_index if idx is not None and idx >= 0) + swap_abxy_indices = {idx for idx in args.swap_abxy_index if idx is not None and idx >= 0} for mapping_path in mappings_to_load: try: loaded = sdl2.SDL_GameControllerAddMappingsFromFile(mapping_path.encode()) - console = Console() console.print(f"[green]Loaded {loaded} SDL mapping(s) from {mapping_path}[/green]") except Exception as exc: - console = Console() console.print(f"[red]Failed to load SDL mapping {mapping_path}: {exc}[/red]") + return button_map_default, button_map_swapped, swap_abxy_indices + +def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeConfig: + interval = 1.0 / max(args.frequency, 1.0) + deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) + trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) + button_map_default, button_map_swapped, swap_abxy_indices = load_button_maps(console, args) + return BridgeConfig( + interval=interval, + deadzone_raw=deadzone_raw, + trigger_threshold=trigger_threshold, + button_map_default=button_map_default, + button_map_swapped=button_map_swapped, + swap_abxy_indices=swap_abxy_indices, + ) + + +def initialize_sdl(parser: argparse.ArgumentParser) -> None: sdl2.SDL_SetHint(sdl2.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, b"1") set_hint("SDL_JOYSTICK_HIDAPI", "1") set_hint("SDL_JOYSTICK_HIDAPI_SWITCH", "1") @@ -531,350 +563,447 @@ def main() -> None: set_hint("SDL_GAMECONTROLLER_USE_BUTTON_LABELS", "1") if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_JOYSTICK | sdl2.SDL_INIT_EVERYTHING) != 0: parser.error(f"SDL init failed: {sdl2.SDL_GetError().decode(errors='ignore')}") + + +def detect_controllers( + console: Console, args: argparse.Namespace, parser: argparse.ArgumentParser +) -> Tuple[List[int], Dict[int, str]]: + controller_indices: List[int] = [] + controller_names: Dict[int, str] = {} + controller_count = sdl2.SDL_NumJoysticks() + if controller_count < 0: + parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") + include_controller_name = [n.lower() for n in args.include_controller_name] + for index in range(controller_count): + if sdl2.SDL_IsGameController(index): + name = sdl2.SDL_GameControllerNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): + console.print(f"[yellow]Skipping controller {index} ({name_str}) due to name filter[/yellow]") + continue + console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") + controller_indices.append(index) + controller_names[index] = name_str + else: + name = sdl2.SDL_JoystickNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): + console.print(f"[yellow]Skipping joystick {index} ({name_str}) due to name filter[/yellow]") + continue + console.print(f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]") + return controller_indices, controller_names + + +def prepare_pairing_state( + args: argparse.Namespace, + console: Console, + parser: argparse.ArgumentParser, + controller_indices: List[int], + controller_names: Dict[int, str], +) -> PairingState: + auto_pairing_enabled = not args.map and not args.interactive + auto_discover_ports = auto_pairing_enabled and not args.ports + include_non_usb = args.all_ports or False + ignore_port_desc = [d.lower() for d in args.ignore_port_desc] + include_port_desc = [d.lower() for d in args.include_port_desc] + available_ports: List[str] = [] + + mappings = list(args.map) + if args.interactive: + if not controller_indices: + parser.error("No controllers detected for interactive pairing.") + discovered = discover_ports( + include_non_usb=include_non_usb, + ignore_descriptions=ignore_port_desc, + include_descriptions=include_port_desc, + ) + if not discovered: + parser.error("No UART devices found for interactive pairing.") + mappings = interactive_pairing(console, controller_names, discovered) + if not mappings: + parser.error("No controller-to-UART mappings were selected.") + elif auto_pairing_enabled: + if args.ports: + available_ports.extend(list(args.ports)) + console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") + else: + discovered = discover_ports( + include_non_usb=include_non_usb, + ignore_descriptions=ignore_port_desc, + include_descriptions=include_port_desc, + ) + if discovered: + available_ports.extend(info["device"] for info in discovered) + console.print("[green]Auto-detected UARTs:[/green]") + for info in discovered: + console.print(f" {info['device']} ({info['description']})") + else: + console.print("[yellow]No UART devices detected yet; waiting for hotplug...[/yellow]") + + mapping_by_index = {index: port for index, port in mappings} + return PairingState( + mapping_by_index=mapping_by_index, + available_ports=available_ports, + auto_pairing_enabled=auto_pairing_enabled, + auto_discover_ports=auto_discover_ports, + include_non_usb=include_non_usb, + ignore_port_desc=ignore_port_desc, + include_port_desc=include_port_desc, + ) + + +def assign_port_for_index(pairing: PairingState, idx: int, console: Console) -> Optional[str]: + if idx in pairing.mapping_by_index: + return pairing.mapping_by_index[idx] + if not pairing.auto_pairing_enabled: + return None + if not pairing.available_ports: + return None + port_choice = pairing.available_ports.pop(0) + pairing.mapping_by_index[idx] = port_choice + pairing.auto_assigned_indices.add(idx) + console.print(f"[green]Auto-paired controller {idx} to {port_choice}[/green]") + return port_choice + + +def ports_in_use(pairing: PairingState, contexts: Dict[int, ControllerContext]) -> set: + used = set(pairing.mapping_by_index.values()) + used.update(ctx.port for ctx in contexts.values() if ctx.port) + return used + + +def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console) -> None: + if not pairing.auto_discover_ports: + return + discovered = discover_ports( + include_non_usb=pairing.include_non_usb, + ignore_descriptions=pairing.ignore_port_desc, + include_descriptions=pairing.include_port_desc, + ) + in_use = ports_in_use(pairing, contexts) + for info in discovered: + path = info["device"] + if path in in_use or path in pairing.available_ports: + continue + pairing.available_ports.append(path) + console.print(f"[green]Discovered UART {path} ({info['description']}); available for pairing.[/green]") + + +def pair_waiting_contexts( + args: argparse.Namespace, + pairing: PairingState, + contexts: Dict[int, ControllerContext], + uarts: List[PicoUART], + console: Console, +) -> None: + for ctx in list(contexts.values()): + if ctx.port is not None: + continue + port_choice = assign_port_for_index(pairing, ctx.controller_index, console) + if port_choice is None: + continue + ctx.port = port_choice + uart = open_uart_or_warn(port_choice, args.baud, console) + ctx.last_reopen_attempt = time.monotonic() + if uart: + uarts.append(uart) + ctx.uart = uart + console.print(f"[green]Controller {ctx.controller_index} ({ctx.instance_id}) paired to {port_choice}[/green]") + else: + ctx.uart = None + console.print(f"[yellow]Controller {ctx.controller_index} ({ctx.instance_id}) waiting for UART {port_choice}[/yellow]") + + +def open_initial_contexts( + args: argparse.Namespace, pairing: PairingState, controller_indices: List[int], console: Console +) -> Tuple[Dict[int, ControllerContext], List[PicoUART]]: contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] - mapping_by_index: Dict[int, str] = {} - console = Console() + for index in controller_indices: + if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): + name = sdl2.SDL_JoystickNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print(f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") + continue + port = assign_port_for_index(pairing, index, console) + if port is None and not pairing.auto_pairing_enabled: + continue + try: + controller, instance_id = open_controller(index) + except Exception as exc: + console.print(f"[red]Failed to open controller {index}: {exc}[/red]") + continue + uart = open_uart_or_warn(port, args.baud, console) if port else None + if uart: + uarts.append(uart) + console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") + elif port: + console.print(f"[yellow]Controller {index} ({instance_id}) waiting for UART {port}[/yellow]") + else: + console.print(f"[yellow]Controller {index} ({instance_id}) connected; waiting for an available UART[/yellow]") + ctx = ControllerContext( + controller=controller, + instance_id=instance_id, + controller_index=index, + port=port, + uart=uart, + ) + contexts[instance_id] = ctx + return contexts, uarts + + +def handle_axis_motion(event: sdl2.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig) -> None: + ctx = contexts.get(event.caxis.which) + if not ctx: + return + axis = event.caxis.axis + value = event.caxis.value + if axis == sdl2.SDL_CONTROLLER_AXIS_LEFTX: + ctx.report.lx = axis_to_stick(value, config.deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_LEFTY: + ctx.report.ly = axis_to_stick(value, config.deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTX: + ctx.report.rx = axis_to_stick(value, config.deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTY: + ctx.report.ry = axis_to_stick(value, config.deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERLEFT: + pressed = trigger_to_button(value, config.trigger_threshold) + if pressed != ctx.last_trigger_state["left"]: + if pressed: + ctx.report.buttons |= SwitchButton.ZL + else: + ctx.report.buttons &= ~SwitchButton.ZL + ctx.last_trigger_state["left"] = pressed + elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + pressed = trigger_to_button(value, config.trigger_threshold) + if pressed != ctx.last_trigger_state["right"]: + if pressed: + ctx.report.buttons |= SwitchButton.ZR + else: + ctx.report.buttons &= ~SwitchButton.ZR + ctx.last_trigger_state["right"] = pressed + + +def handle_button_event( + event: sdl2.SDL_Event, + args: argparse.Namespace, + config: BridgeConfig, + contexts: Dict[int, ControllerContext], +) -> None: + ctx = contexts.get(event.cbutton.which) + if not ctx: + return + current_button_map = ( + config.button_map_swapped + if (args.swap_abxy or ctx.controller_index in config.swap_abxy_indices) + else config.button_map_default + ) + button = event.cbutton.button + pressed = event.type == sdl2.SDL_CONTROLLERBUTTONDOWN + if button in current_button_map: + bit = current_button_map[button] + if pressed: + ctx.report.buttons |= bit + else: + ctx.report.buttons &= ~bit + ctx.button_state[button] = pressed + elif button in DPAD_BUTTONS: + ctx.dpad[DPAD_BUTTONS[button]] = pressed + ctx.report.hat = dpad_to_hat(ctx.dpad) + + +def handle_device_added( + event: sdl2.SDL_Event, + args: argparse.Namespace, + pairing: PairingState, + contexts: Dict[int, ControllerContext], + uarts: List[PicoUART], + console: Console, +) -> None: + idx = event.cdevice.which + if any(c.controller_index == idx for c in contexts.values()): + return + port = assign_port_for_index(pairing, idx, console) + if port is None and not pairing.auto_pairing_enabled: + return + if idx >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(idx): + name = sdl2.SDL_JoystickNameForIndex(idx) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print(f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") + return try: - controller_indices: List[int] = [] - controller_names: Dict[int, str] = {} - controller_count = sdl2.SDL_NumJoysticks() - if controller_count < 0: - parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") - auto_pairing_enabled = not args.map and not args.interactive - auto_discover_ports = auto_pairing_enabled and not args.ports - available_ports: List[str] = [] - auto_assigned_indices: set[int] = set() - include_non_usb = args.all_ports or False - ignore_port_desc = [d.lower() for d in args.ignore_port_desc] - include_port_desc = [d.lower() for d in args.include_port_desc] - include_controller_name = [n.lower() for n in args.include_controller_name] - for index in range(controller_count): - if sdl2.SDL_IsGameController(index): - name = sdl2.SDL_GameControllerNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) - if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): - console.print(f"[yellow]Skipping controller {index} ({name_str}) due to name filter[/yellow]") - continue - console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") - controller_indices.append(index) - controller_names[index] = name_str - else: - name = sdl2.SDL_JoystickNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) - if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): - console.print(f"[yellow]Skipping joystick {index} ({name_str}) due to name filter[/yellow]") - continue - console.print(f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]") + controller, instance_id = open_controller(idx) + except Exception as exc: + console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") + return + uart = open_uart_or_warn(port, args.baud, console) if port else None + if uart: + uarts.append(uart) + console.print(f"[green]Controller {idx} ({instance_id}) paired to {port}[/green]") + elif port: + console.print(f"[yellow]Controller {idx} ({instance_id}) waiting for UART {port}[/yellow]") + else: + console.print(f"[yellow]Controller {idx} ({instance_id}) connected; waiting for an available UART[/yellow]") + ctx = ControllerContext( + controller=controller, + instance_id=instance_id, + controller_index=idx, + port=port, + uart=uart, + ) + contexts[instance_id] = ctx - mappings = list(args.map) - if args.interactive: - if not controller_indices: - parser.error("No controllers detected for interactive pairing.") - discovered = discover_ports( - include_non_usb=include_non_usb, - ignore_descriptions=ignore_port_desc, - include_descriptions=include_port_desc, - ) - if not discovered: - parser.error("No UART devices found for interactive pairing.") - mappings = interactive_pairing(console, controller_names, discovered) - if not mappings: - parser.error("No controller-to-UART mappings were selected.") - elif auto_pairing_enabled: - if args.ports: - available_ports.extend(list(args.ports)) - console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") - else: - discovered = discover_ports( - include_non_usb=include_non_usb, - ignore_descriptions=ignore_port_desc, - include_descriptions=include_port_desc, - ) - if discovered: - available_ports.extend(info["device"] for info in discovered) - console.print("[green]Auto-detected UARTs:[/green]") - for info in discovered: - console.print(f" {info['device']} ({info['description']})") - else: - console.print("[yellow]No UART devices detected yet; waiting for hotplug...[/yellow]") - for index, port in mappings: - mapping_by_index[index] = port +def handle_device_removed( + event: sdl2.SDL_Event, + pairing: PairingState, + contexts: Dict[int, ControllerContext], + console: Console, +) -> None: + instance_id = event.cdevice.which + ctx = contexts.pop(instance_id, None) + if not ctx: + return + console.print(f"[yellow]Controller {instance_id} removed[/yellow]") + if ctx.controller_index in pairing.auto_assigned_indices: + freed = pairing.mapping_by_index.pop(ctx.controller_index, None) + pairing.auto_assigned_indices.discard(ctx.controller_index) + if freed and freed not in pairing.available_ports: + pairing.available_ports.append(freed) + console.print(f"[cyan]Released UART {freed} back to pool[/cyan]") + sdl2.SDL_GameControllerClose(ctx.controller) - def assign_port_for_index(idx: int) -> Optional[str]: - if idx in mapping_by_index: - return mapping_by_index[idx] - if not auto_pairing_enabled: - return None - if not available_ports: - return None - port_choice = available_ports.pop(0) - mapping_by_index[idx] = port_choice - auto_assigned_indices.add(idx) - console.print(f"[green]Auto-paired controller {idx} to {port_choice}[/green]") - return port_choice - # Open currently connected controllers that we can pair. - for index in controller_indices: - if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): - name = sdl2.SDL_JoystickNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) - console.print(f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") - continue - port = assign_port_for_index(index) - if port is None and not auto_pairing_enabled: - continue - try: - controller, instance_id = open_controller(index) - except Exception as exc: - console.print(f"[red]Failed to open controller {index}: {exc}[/red]") - continue - uart = open_uart_or_warn(port, args.baud, console) if port else None +def service_contexts( + now: float, + args: argparse.Namespace, + config: BridgeConfig, + contexts: Dict[int, ControllerContext], + uarts: List[PicoUART], + console: Console, +) -> None: + for ctx in list(contexts.values()): + current_button_map = ( + config.button_map_swapped + if (args.swap_abxy or ctx.controller_index in config.swap_abxy_indices) + else config.button_map_default + ) + poll_controller_buttons(ctx, current_button_map) + # Reconnect UART if needed. + if ctx.port and ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: + ctx.last_reopen_attempt = now + uart = open_uart_or_warn(ctx.port, args.baud, console) if uart: uarts.append(uart) - console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") - elif port: - console.print(f"[yellow]Controller {index} ({instance_id}) waiting for UART {port}[/yellow]") - else: - console.print(f"[yellow]Controller {index} ({instance_id}) connected; waiting for an available UART[/yellow]") - ctx = ControllerContext( - controller=controller, - instance_id=instance_id, - controller_index=index, - port=port, - uart=uart, - ) - contexts[instance_id] = ctx + console.print(f"[green]Reconnected UART {ctx.port} for controller {ctx.controller_index}[/green]") + ctx.uart = uart + if ctx.uart is None: + continue + try: + if now - ctx.last_send >= config.interval: + ctx.uart.send_report(ctx.report) + ctx.last_send = now + last_payload = None + while True: + p = ctx.uart.read_rumble_payload() + if not p: + break + last_payload = p + + if last_payload is not None: + energy = apply_rumble(ctx.controller, last_payload) + ctx.rumble_active = energy >= RUMBLE_MIN_ACTIVE + if ctx.rumble_active and energy != ctx.last_rumble_energy: + ctx.last_rumble_change = now + ctx.last_rumble_energy = energy + ctx.last_rumble = now + elif ctx.rumble_active and (now - ctx.last_rumble) > RUMBLE_IDLE_TIMEOUT: + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 + elif ctx.rumble_active and (now - ctx.last_rumble_change) > RUMBLE_STUCK_TIMEOUT: + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 + except SerialException as exc: + console.print(f"[yellow]UART {ctx.port} disconnected: {exc}[/yellow]") + try: + ctx.uart.close() + except Exception: + pass + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + ctx.uart = None + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 + ctx.last_reopen_attempt = now + except Exception as exc: + console.print(f"[red]UART error on {ctx.port}: {exc}[/red]") + + +def run_bridge_loop( + args: argparse.Namespace, + console: Console, + config: BridgeConfig, + pairing: PairingState, + contexts: Dict[int, ControllerContext], + uarts: List[PicoUART], +) -> None: + event = sdl2.SDL_Event() + port_scan_interval = 2.0 + last_port_scan = time.monotonic() + running = True + + while running: + while sdl2.SDL_PollEvent(event): + if event.type == sdl2.SDL_QUIT: + running = False + break + if event.type == sdl2.SDL_CONTROLLERAXISMOTION: + handle_axis_motion(event, contexts, config) + elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): + handle_button_event(event, args, config, contexts) + elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: + handle_device_added(event, args, pairing, contexts, uarts, console) + elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: + handle_device_removed(event, pairing, contexts, console) + + now = time.monotonic() + if now - last_port_scan > port_scan_interval: + discover_new_ports(pairing, contexts, console) + last_port_scan = now + pair_waiting_contexts(args, pairing, contexts, uarts, console) + else: + pair_waiting_contexts(args, pairing, contexts, uarts, console) + service_contexts(now, args, config, contexts, uarts, console) + sdl2.SDL_Delay(1) + + +def cleanup(contexts: Dict[int, ControllerContext], uarts: List[PicoUART]) -> None: + for ctx in contexts.values(): + sdl2.SDL_GameControllerClose(ctx.controller) + for uart in uarts: + uart.close() + sdl2.SDL_Quit() + + +def main() -> None: + parser = build_arg_parser() + args = parser.parse_args() + console = Console() + config = build_bridge_config(console, args) + initialize_sdl(parser) + contexts: Dict[int, ControllerContext] = {} + uarts: List[PicoUART] = [] + try: + controller_indices, controller_names = detect_controllers(console, args, parser) + pairing = prepare_pairing_state(args, console, parser, controller_indices, controller_names) + contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console) if not contexts: console.print("[yellow]No controllers opened; waiting for hotplug events...[/yellow]") - - def ports_in_use() -> set: - used = set(mapping_by_index.values()) - used.update(ctx.port for ctx in contexts.values() if ctx.port) - return used - - def discover_new_ports() -> None: - if not auto_discover_ports: - return - discovered = discover_ports( - include_non_usb=include_non_usb, - ignore_descriptions=ignore_port_desc, - include_descriptions=include_port_desc, - ) - in_use = ports_in_use() - for info in discovered: - path = info["device"] - if path in in_use or path in available_ports: - continue - available_ports.append(path) - console.print(f"[green]Discovered UART {path} ({info['description']}); available for pairing.[/green]") - - def pair_waiting_contexts() -> None: - for ctx in list(contexts.values()): - if ctx.port is not None: - continue - port_choice = assign_port_for_index(ctx.controller_index) - if port_choice is None: - continue - ctx.port = port_choice - uart = open_uart_or_warn(port_choice, args.baud, console) - ctx.last_reopen_attempt = time.monotonic() - if uart: - uarts.append(uart) - ctx.uart = uart - console.print( - f"[green]Controller {ctx.controller_index} ({ctx.instance_id}) paired to {port_choice}[/green]" - ) - else: - ctx.uart = None - console.print( - f"[yellow]Controller {ctx.controller_index} ({ctx.instance_id}) waiting for UART {port_choice}[/yellow]" - ) - - event = sdl2.SDL_Event() - port_scan_interval = 2.0 - last_port_scan = time.monotonic() - running = True - while running: - while sdl2.SDL_PollEvent(event): - if event.type == sdl2.SDL_QUIT: - running = False - break - if event.type == sdl2.SDL_CONTROLLERAXISMOTION: - ctx = contexts.get(event.caxis.which) - if not ctx: - continue - axis = event.caxis.axis - value = event.caxis.value - if axis == sdl2.SDL_CONTROLLER_AXIS_LEFTX: - ctx.report.lx = axis_to_stick(value, deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_LEFTY: - ctx.report.ly = axis_to_stick(value, deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTX: - ctx.report.rx = axis_to_stick(value, deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTY: - ctx.report.ry = axis_to_stick(value, deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERLEFT: - pressed = trigger_to_button(value, trigger_threshold) - if pressed != ctx.last_trigger_state["left"]: - if pressed: - ctx.report.buttons |= SwitchButton.ZL - else: - ctx.report.buttons &= ~SwitchButton.ZL - ctx.last_trigger_state["left"] = pressed - elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERRIGHT: - pressed = trigger_to_button(value, trigger_threshold) - if pressed != ctx.last_trigger_state["right"]: - if pressed: - ctx.report.buttons |= SwitchButton.ZR - else: - ctx.report.buttons &= ~SwitchButton.ZR - ctx.last_trigger_state["right"] = pressed - elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): - ctx = contexts.get(event.cbutton.which) - if not ctx: - continue - current_button_map = ( - button_map_swapped - if (args.swap_abxy or ctx.controller_index in swap_abxy_indices) - else button_map_default - ) - button = event.cbutton.button - pressed = event.type == sdl2.SDL_CONTROLLERBUTTONDOWN - if button in current_button_map: - bit = current_button_map[button] - if pressed: - ctx.report.buttons |= bit - else: - ctx.report.buttons &= ~bit - ctx.button_state[button] = pressed - elif button in DPAD_BUTTONS: - ctx.dpad[DPAD_BUTTONS[button]] = pressed - ctx.report.hat = dpad_to_hat(ctx.dpad) - elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: - idx = event.cdevice.which - if any(c.controller_index == idx for c in contexts.values()): - continue - port = assign_port_for_index(idx) - if port is None and not auto_pairing_enabled: - continue - if idx >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(idx): - name = sdl2.SDL_JoystickNameForIndex(idx) - name_str = name.decode() if isinstance(name, bytes) else str(name) - console.print( - f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" - ) - continue - try: - controller, instance_id = open_controller(idx) - except Exception as exc: - console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") - continue - uart = open_uart_or_warn(port, args.baud, console) if port else None - if uart: - uarts.append(uart) - console.print(f"[green]Controller {idx} ({instance_id}) paired to {port}[/green]") - elif port: - console.print(f"[yellow]Controller {idx} ({instance_id}) waiting for UART {port}[/yellow]") - else: - console.print(f"[yellow]Controller {idx} ({instance_id}) connected; waiting for an available UART[/yellow]") - ctx = ControllerContext( - controller=controller, - instance_id=instance_id, - controller_index=idx, - port=port, - uart=uart, - ) - contexts[instance_id] = ctx - elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: - instance_id = event.cdevice.which - ctx = contexts.pop(instance_id, None) - if ctx: - console.print(f"[yellow]Controller {instance_id} removed[/yellow]") - if ctx.controller_index in auto_assigned_indices: - freed = mapping_by_index.pop(ctx.controller_index, None) - auto_assigned_indices.discard(ctx.controller_index) - if freed and freed not in available_ports: - available_ports.append(freed) - console.print(f"[cyan]Released UART {freed} back to pool[/cyan]") - sdl2.SDL_GameControllerClose(ctx.controller) - - now = time.monotonic() - if now - last_port_scan > port_scan_interval: - discover_new_ports() - last_port_scan = now - pair_waiting_contexts() - else: - pair_waiting_contexts() - for ctx in list(contexts.values()): - current_button_map = ( - button_map_swapped - if (args.swap_abxy or ctx.controller_index in swap_abxy_indices) - else button_map_default - ) - poll_controller_buttons(ctx, current_button_map) - # Reconnect UART if needed. - if ctx.port and ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: - ctx.last_reopen_attempt = now - uart = open_uart_or_warn(ctx.port, args.baud, console) - if uart: - uarts.append(uart) - console.print(f"[green]Reconnected UART {ctx.port} for controller {ctx.controller_index}[/green]") - ctx.uart = uart - if ctx.uart is None: - continue - try: - # Send controller state at configured frequency - if now - ctx.last_send >= interval: - ctx.uart.send_report(ctx.report) - ctx.last_send = now - - # Drain all pending rumble frames and apply only the latest one. - last_payload = None - while True: - p = ctx.uart.read_rumble_payload() - if not p: - break - last_payload = p - - if last_payload is not None: - energy = apply_rumble(ctx.controller, last_payload) - ctx.rumble_active = energy >= RUMBLE_MIN_ACTIVE - if ctx.rumble_active and energy != ctx.last_rumble_energy: - ctx.last_rumble_change = now - ctx.last_rumble_energy = energy - ctx.last_rumble = now - elif ctx.rumble_active and (now - ctx.last_rumble) > RUMBLE_IDLE_TIMEOUT: - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) - ctx.rumble_active = False - ctx.last_rumble_energy = 0.0 - elif ctx.rumble_active and (now - ctx.last_rumble_change) > RUMBLE_STUCK_TIMEOUT: - # Guard against a stream of tiny-but-nonzero rumble packets that never decay. - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) - ctx.rumble_active = False - ctx.last_rumble_energy = 0.0 - except SerialException as exc: - console.print(f"[yellow]UART {ctx.port} disconnected: {exc}[/yellow]") - try: - ctx.uart.close() - except Exception: - pass - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) - ctx.uart = None - ctx.rumble_active = False - ctx.last_rumble_energy = 0.0 - ctx.last_reopen_attempt = now - except Exception as exc: - console.print(f"[red]UART error on {ctx.port}: {exc}[/red]") - sdl2.SDL_Delay(1) + run_bridge_loop(args, console, config, pairing, contexts, uarts) finally: - for ctx in contexts.values(): - sdl2.SDL_GameControllerClose(ctx.controller) - for uart in uarts: - uart.close() - sdl2.SDL_Quit() + cleanup(contexts, uarts) if __name__ == "__main__": From 30aa3efc81dabe0622200714c6e41e288c9b6c3d Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 23 Nov 2025 09:27:16 -0700 Subject: [PATCH 042/134] Refactor --- controller_uart_bridge.py | 813 ++++++++++++++++++++++---------------- 1 file changed, 471 insertions(+), 342 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index dd38821..81f77da 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -495,16 +495,32 @@ def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, int]) ctx.report.hat = dpad_to_hat(ctx.dpad) -def main() -> None: - parser = build_arg_parser() - args = parser.parse_args() - interval = 1.0 / max(args.frequency, 1.0) - deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) - trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) +@dataclass +class BridgeConfig: + interval: float + deadzone_raw: int + trigger_threshold: int + button_map_default: Dict[int, int] + button_map_swapped: Dict[int, int] + swap_abxy_indices: set[int] - # Load bundled mapping plus any user-supplied mapping files. + +@dataclass +class PairingState: + mapping_by_index: Dict[int, str] + available_ports: List[str] + auto_assigned_indices: set[int] = field(default_factory=set) + auto_pairing_enabled: bool = False + auto_discover_ports: bool = False + include_non_usb: bool = False + ignore_port_desc: List[str] = field(default_factory=list) + include_port_desc: List[str] = field(default_factory=list) + + +def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, int], Dict[int, int], set[int]]: + """Load SDL controller mappings and return button map variants.""" default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" - mappings_to_load = [] + mappings_to_load: List[str] = [] if default_mapping.exists(): mappings_to_load.append(str(default_mapping)) mappings_to_load.extend(args.sdl_mapping) @@ -514,16 +530,32 @@ def main() -> None: button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_B] = SwitchButton.A button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_X] = SwitchButton.Y button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_Y] = SwitchButton.X - swap_abxy_indices = set(idx for idx in args.swap_abxy_index if idx is not None and idx >= 0) + swap_abxy_indices = {idx for idx in args.swap_abxy_index if idx is not None and idx >= 0} for mapping_path in mappings_to_load: try: loaded = sdl2.SDL_GameControllerAddMappingsFromFile(mapping_path.encode()) - console = Console() console.print(f"[green]Loaded {loaded} SDL mapping(s) from {mapping_path}[/green]") except Exception as exc: - console = Console() console.print(f"[red]Failed to load SDL mapping {mapping_path}: {exc}[/red]") + return button_map_default, button_map_swapped, swap_abxy_indices + +def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeConfig: + interval = 1.0 / max(args.frequency, 1.0) + deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) + trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) + button_map_default, button_map_swapped, swap_abxy_indices = load_button_maps(console, args) + return BridgeConfig( + interval=interval, + deadzone_raw=deadzone_raw, + trigger_threshold=trigger_threshold, + button_map_default=button_map_default, + button_map_swapped=button_map_swapped, + swap_abxy_indices=swap_abxy_indices, + ) + + +def initialize_sdl(parser: argparse.ArgumentParser) -> None: sdl2.SDL_SetHint(sdl2.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, b"1") set_hint("SDL_JOYSTICK_HIDAPI", "1") set_hint("SDL_JOYSTICK_HIDAPI_SWITCH", "1") @@ -531,350 +563,447 @@ def main() -> None: set_hint("SDL_GAMECONTROLLER_USE_BUTTON_LABELS", "1") if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_JOYSTICK | sdl2.SDL_INIT_EVERYTHING) != 0: parser.error(f"SDL init failed: {sdl2.SDL_GetError().decode(errors='ignore')}") + + +def detect_controllers( + console: Console, args: argparse.Namespace, parser: argparse.ArgumentParser +) -> Tuple[List[int], Dict[int, str]]: + controller_indices: List[int] = [] + controller_names: Dict[int, str] = {} + controller_count = sdl2.SDL_NumJoysticks() + if controller_count < 0: + parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") + include_controller_name = [n.lower() for n in args.include_controller_name] + for index in range(controller_count): + if sdl2.SDL_IsGameController(index): + name = sdl2.SDL_GameControllerNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): + console.print(f"[yellow]Skipping controller {index} ({name_str}) due to name filter[/yellow]") + continue + console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") + controller_indices.append(index) + controller_names[index] = name_str + else: + name = sdl2.SDL_JoystickNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): + console.print(f"[yellow]Skipping joystick {index} ({name_str}) due to name filter[/yellow]") + continue + console.print(f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]") + return controller_indices, controller_names + + +def prepare_pairing_state( + args: argparse.Namespace, + console: Console, + parser: argparse.ArgumentParser, + controller_indices: List[int], + controller_names: Dict[int, str], +) -> PairingState: + auto_pairing_enabled = not args.map and not args.interactive + auto_discover_ports = auto_pairing_enabled and not args.ports + include_non_usb = args.all_ports or False + ignore_port_desc = [d.lower() for d in args.ignore_port_desc] + include_port_desc = [d.lower() for d in args.include_port_desc] + available_ports: List[str] = [] + + mappings = list(args.map) + if args.interactive: + if not controller_indices: + parser.error("No controllers detected for interactive pairing.") + discovered = discover_ports( + include_non_usb=include_non_usb, + ignore_descriptions=ignore_port_desc, + include_descriptions=include_port_desc, + ) + if not discovered: + parser.error("No UART devices found for interactive pairing.") + mappings = interactive_pairing(console, controller_names, discovered) + if not mappings: + parser.error("No controller-to-UART mappings were selected.") + elif auto_pairing_enabled: + if args.ports: + available_ports.extend(list(args.ports)) + console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") + else: + discovered = discover_ports( + include_non_usb=include_non_usb, + ignore_descriptions=ignore_port_desc, + include_descriptions=include_port_desc, + ) + if discovered: + available_ports.extend(info["device"] for info in discovered) + console.print("[green]Auto-detected UARTs:[/green]") + for info in discovered: + console.print(f" {info['device']} ({info['description']})") + else: + console.print("[yellow]No UART devices detected yet; waiting for hotplug...[/yellow]") + + mapping_by_index = {index: port for index, port in mappings} + return PairingState( + mapping_by_index=mapping_by_index, + available_ports=available_ports, + auto_pairing_enabled=auto_pairing_enabled, + auto_discover_ports=auto_discover_ports, + include_non_usb=include_non_usb, + ignore_port_desc=ignore_port_desc, + include_port_desc=include_port_desc, + ) + + +def assign_port_for_index(pairing: PairingState, idx: int, console: Console) -> Optional[str]: + if idx in pairing.mapping_by_index: + return pairing.mapping_by_index[idx] + if not pairing.auto_pairing_enabled: + return None + if not pairing.available_ports: + return None + port_choice = pairing.available_ports.pop(0) + pairing.mapping_by_index[idx] = port_choice + pairing.auto_assigned_indices.add(idx) + console.print(f"[green]Auto-paired controller {idx} to {port_choice}[/green]") + return port_choice + + +def ports_in_use(pairing: PairingState, contexts: Dict[int, ControllerContext]) -> set: + used = set(pairing.mapping_by_index.values()) + used.update(ctx.port for ctx in contexts.values() if ctx.port) + return used + + +def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console) -> None: + if not pairing.auto_discover_ports: + return + discovered = discover_ports( + include_non_usb=pairing.include_non_usb, + ignore_descriptions=pairing.ignore_port_desc, + include_descriptions=pairing.include_port_desc, + ) + in_use = ports_in_use(pairing, contexts) + for info in discovered: + path = info["device"] + if path in in_use or path in pairing.available_ports: + continue + pairing.available_ports.append(path) + console.print(f"[green]Discovered UART {path} ({info['description']}); available for pairing.[/green]") + + +def pair_waiting_contexts( + args: argparse.Namespace, + pairing: PairingState, + contexts: Dict[int, ControllerContext], + uarts: List[PicoUART], + console: Console, +) -> None: + for ctx in list(contexts.values()): + if ctx.port is not None: + continue + port_choice = assign_port_for_index(pairing, ctx.controller_index, console) + if port_choice is None: + continue + ctx.port = port_choice + uart = open_uart_or_warn(port_choice, args.baud, console) + ctx.last_reopen_attempt = time.monotonic() + if uart: + uarts.append(uart) + ctx.uart = uart + console.print(f"[green]Controller {ctx.controller_index} ({ctx.instance_id}) paired to {port_choice}[/green]") + else: + ctx.uart = None + console.print(f"[yellow]Controller {ctx.controller_index} ({ctx.instance_id}) waiting for UART {port_choice}[/yellow]") + + +def open_initial_contexts( + args: argparse.Namespace, pairing: PairingState, controller_indices: List[int], console: Console +) -> Tuple[Dict[int, ControllerContext], List[PicoUART]]: contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] - mapping_by_index: Dict[int, str] = {} - console = Console() + for index in controller_indices: + if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): + name = sdl2.SDL_JoystickNameForIndex(index) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print(f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") + continue + port = assign_port_for_index(pairing, index, console) + if port is None and not pairing.auto_pairing_enabled: + continue + try: + controller, instance_id = open_controller(index) + except Exception as exc: + console.print(f"[red]Failed to open controller {index}: {exc}[/red]") + continue + uart = open_uart_or_warn(port, args.baud, console) if port else None + if uart: + uarts.append(uart) + console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") + elif port: + console.print(f"[yellow]Controller {index} ({instance_id}) waiting for UART {port}[/yellow]") + else: + console.print(f"[yellow]Controller {index} ({instance_id}) connected; waiting for an available UART[/yellow]") + ctx = ControllerContext( + controller=controller, + instance_id=instance_id, + controller_index=index, + port=port, + uart=uart, + ) + contexts[instance_id] = ctx + return contexts, uarts + + +def handle_axis_motion(event: sdl2.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig) -> None: + ctx = contexts.get(event.caxis.which) + if not ctx: + return + axis = event.caxis.axis + value = event.caxis.value + if axis == sdl2.SDL_CONTROLLER_AXIS_LEFTX: + ctx.report.lx = axis_to_stick(value, config.deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_LEFTY: + ctx.report.ly = axis_to_stick(value, config.deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTX: + ctx.report.rx = axis_to_stick(value, config.deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTY: + ctx.report.ry = axis_to_stick(value, config.deadzone_raw) + elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERLEFT: + pressed = trigger_to_button(value, config.trigger_threshold) + if pressed != ctx.last_trigger_state["left"]: + if pressed: + ctx.report.buttons |= SwitchButton.ZL + else: + ctx.report.buttons &= ~SwitchButton.ZL + ctx.last_trigger_state["left"] = pressed + elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + pressed = trigger_to_button(value, config.trigger_threshold) + if pressed != ctx.last_trigger_state["right"]: + if pressed: + ctx.report.buttons |= SwitchButton.ZR + else: + ctx.report.buttons &= ~SwitchButton.ZR + ctx.last_trigger_state["right"] = pressed + + +def handle_button_event( + event: sdl2.SDL_Event, + args: argparse.Namespace, + config: BridgeConfig, + contexts: Dict[int, ControllerContext], +) -> None: + ctx = contexts.get(event.cbutton.which) + if not ctx: + return + current_button_map = ( + config.button_map_swapped + if (args.swap_abxy or ctx.controller_index in config.swap_abxy_indices) + else config.button_map_default + ) + button = event.cbutton.button + pressed = event.type == sdl2.SDL_CONTROLLERBUTTONDOWN + if button in current_button_map: + bit = current_button_map[button] + if pressed: + ctx.report.buttons |= bit + else: + ctx.report.buttons &= ~bit + ctx.button_state[button] = pressed + elif button in DPAD_BUTTONS: + ctx.dpad[DPAD_BUTTONS[button]] = pressed + ctx.report.hat = dpad_to_hat(ctx.dpad) + + +def handle_device_added( + event: sdl2.SDL_Event, + args: argparse.Namespace, + pairing: PairingState, + contexts: Dict[int, ControllerContext], + uarts: List[PicoUART], + console: Console, +) -> None: + idx = event.cdevice.which + if any(c.controller_index == idx for c in contexts.values()): + return + port = assign_port_for_index(pairing, idx, console) + if port is None and not pairing.auto_pairing_enabled: + return + if idx >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(idx): + name = sdl2.SDL_JoystickNameForIndex(idx) + name_str = name.decode() if isinstance(name, bytes) else str(name) + console.print(f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") + return try: - controller_indices: List[int] = [] - controller_names: Dict[int, str] = {} - controller_count = sdl2.SDL_NumJoysticks() - if controller_count < 0: - parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") - auto_pairing_enabled = not args.map and not args.interactive - auto_discover_ports = auto_pairing_enabled and not args.ports - available_ports: List[str] = [] - auto_assigned_indices: set[int] = set() - include_non_usb = args.all_ports or False - ignore_port_desc = [d.lower() for d in args.ignore_port_desc] - include_port_desc = [d.lower() for d in args.include_port_desc] - include_controller_name = [n.lower() for n in args.include_controller_name] - for index in range(controller_count): - if sdl2.SDL_IsGameController(index): - name = sdl2.SDL_GameControllerNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) - if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): - console.print(f"[yellow]Skipping controller {index} ({name_str}) due to name filter[/yellow]") - continue - console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") - controller_indices.append(index) - controller_names[index] = name_str - else: - name = sdl2.SDL_JoystickNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) - if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): - console.print(f"[yellow]Skipping joystick {index} ({name_str}) due to name filter[/yellow]") - continue - console.print(f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]") + controller, instance_id = open_controller(idx) + except Exception as exc: + console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") + return + uart = open_uart_or_warn(port, args.baud, console) if port else None + if uart: + uarts.append(uart) + console.print(f"[green]Controller {idx} ({instance_id}) paired to {port}[/green]") + elif port: + console.print(f"[yellow]Controller {idx} ({instance_id}) waiting for UART {port}[/yellow]") + else: + console.print(f"[yellow]Controller {idx} ({instance_id}) connected; waiting for an available UART[/yellow]") + ctx = ControllerContext( + controller=controller, + instance_id=instance_id, + controller_index=idx, + port=port, + uart=uart, + ) + contexts[instance_id] = ctx - mappings = list(args.map) - if args.interactive: - if not controller_indices: - parser.error("No controllers detected for interactive pairing.") - discovered = discover_ports( - include_non_usb=include_non_usb, - ignore_descriptions=ignore_port_desc, - include_descriptions=include_port_desc, - ) - if not discovered: - parser.error("No UART devices found for interactive pairing.") - mappings = interactive_pairing(console, controller_names, discovered) - if not mappings: - parser.error("No controller-to-UART mappings were selected.") - elif auto_pairing_enabled: - if args.ports: - available_ports.extend(list(args.ports)) - console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") - else: - discovered = discover_ports( - include_non_usb=include_non_usb, - ignore_descriptions=ignore_port_desc, - include_descriptions=include_port_desc, - ) - if discovered: - available_ports.extend(info["device"] for info in discovered) - console.print("[green]Auto-detected UARTs:[/green]") - for info in discovered: - console.print(f" {info['device']} ({info['description']})") - else: - console.print("[yellow]No UART devices detected yet; waiting for hotplug...[/yellow]") - for index, port in mappings: - mapping_by_index[index] = port +def handle_device_removed( + event: sdl2.SDL_Event, + pairing: PairingState, + contexts: Dict[int, ControllerContext], + console: Console, +) -> None: + instance_id = event.cdevice.which + ctx = contexts.pop(instance_id, None) + if not ctx: + return + console.print(f"[yellow]Controller {instance_id} removed[/yellow]") + if ctx.controller_index in pairing.auto_assigned_indices: + freed = pairing.mapping_by_index.pop(ctx.controller_index, None) + pairing.auto_assigned_indices.discard(ctx.controller_index) + if freed and freed not in pairing.available_ports: + pairing.available_ports.append(freed) + console.print(f"[cyan]Released UART {freed} back to pool[/cyan]") + sdl2.SDL_GameControllerClose(ctx.controller) - def assign_port_for_index(idx: int) -> Optional[str]: - if idx in mapping_by_index: - return mapping_by_index[idx] - if not auto_pairing_enabled: - return None - if not available_ports: - return None - port_choice = available_ports.pop(0) - mapping_by_index[idx] = port_choice - auto_assigned_indices.add(idx) - console.print(f"[green]Auto-paired controller {idx} to {port_choice}[/green]") - return port_choice - # Open currently connected controllers that we can pair. - for index in controller_indices: - if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): - name = sdl2.SDL_JoystickNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) - console.print(f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") - continue - port = assign_port_for_index(index) - if port is None and not auto_pairing_enabled: - continue - try: - controller, instance_id = open_controller(index) - except Exception as exc: - console.print(f"[red]Failed to open controller {index}: {exc}[/red]") - continue - uart = open_uart_or_warn(port, args.baud, console) if port else None +def service_contexts( + now: float, + args: argparse.Namespace, + config: BridgeConfig, + contexts: Dict[int, ControllerContext], + uarts: List[PicoUART], + console: Console, +) -> None: + for ctx in list(contexts.values()): + current_button_map = ( + config.button_map_swapped + if (args.swap_abxy or ctx.controller_index in config.swap_abxy_indices) + else config.button_map_default + ) + poll_controller_buttons(ctx, current_button_map) + # Reconnect UART if needed. + if ctx.port and ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: + ctx.last_reopen_attempt = now + uart = open_uart_or_warn(ctx.port, args.baud, console) if uart: uarts.append(uart) - console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") - elif port: - console.print(f"[yellow]Controller {index} ({instance_id}) waiting for UART {port}[/yellow]") - else: - console.print(f"[yellow]Controller {index} ({instance_id}) connected; waiting for an available UART[/yellow]") - ctx = ControllerContext( - controller=controller, - instance_id=instance_id, - controller_index=index, - port=port, - uart=uart, - ) - contexts[instance_id] = ctx + console.print(f"[green]Reconnected UART {ctx.port} for controller {ctx.controller_index}[/green]") + ctx.uart = uart + if ctx.uart is None: + continue + try: + if now - ctx.last_send >= config.interval: + ctx.uart.send_report(ctx.report) + ctx.last_send = now + last_payload = None + while True: + p = ctx.uart.read_rumble_payload() + if not p: + break + last_payload = p + + if last_payload is not None: + energy = apply_rumble(ctx.controller, last_payload) + ctx.rumble_active = energy >= RUMBLE_MIN_ACTIVE + if ctx.rumble_active and energy != ctx.last_rumble_energy: + ctx.last_rumble_change = now + ctx.last_rumble_energy = energy + ctx.last_rumble = now + elif ctx.rumble_active and (now - ctx.last_rumble) > RUMBLE_IDLE_TIMEOUT: + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 + elif ctx.rumble_active and (now - ctx.last_rumble_change) > RUMBLE_STUCK_TIMEOUT: + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 + except SerialException as exc: + console.print(f"[yellow]UART {ctx.port} disconnected: {exc}[/yellow]") + try: + ctx.uart.close() + except Exception: + pass + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + ctx.uart = None + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 + ctx.last_reopen_attempt = now + except Exception as exc: + console.print(f"[red]UART error on {ctx.port}: {exc}[/red]") + + +def run_bridge_loop( + args: argparse.Namespace, + console: Console, + config: BridgeConfig, + pairing: PairingState, + contexts: Dict[int, ControllerContext], + uarts: List[PicoUART], +) -> None: + event = sdl2.SDL_Event() + port_scan_interval = 2.0 + last_port_scan = time.monotonic() + running = True + + while running: + while sdl2.SDL_PollEvent(event): + if event.type == sdl2.SDL_QUIT: + running = False + break + if event.type == sdl2.SDL_CONTROLLERAXISMOTION: + handle_axis_motion(event, contexts, config) + elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): + handle_button_event(event, args, config, contexts) + elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: + handle_device_added(event, args, pairing, contexts, uarts, console) + elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: + handle_device_removed(event, pairing, contexts, console) + + now = time.monotonic() + if now - last_port_scan > port_scan_interval: + discover_new_ports(pairing, contexts, console) + last_port_scan = now + pair_waiting_contexts(args, pairing, contexts, uarts, console) + else: + pair_waiting_contexts(args, pairing, contexts, uarts, console) + service_contexts(now, args, config, contexts, uarts, console) + sdl2.SDL_Delay(1) + + +def cleanup(contexts: Dict[int, ControllerContext], uarts: List[PicoUART]) -> None: + for ctx in contexts.values(): + sdl2.SDL_GameControllerClose(ctx.controller) + for uart in uarts: + uart.close() + sdl2.SDL_Quit() + + +def main() -> None: + parser = build_arg_parser() + args = parser.parse_args() + console = Console() + config = build_bridge_config(console, args) + initialize_sdl(parser) + contexts: Dict[int, ControllerContext] = {} + uarts: List[PicoUART] = [] + try: + controller_indices, controller_names = detect_controllers(console, args, parser) + pairing = prepare_pairing_state(args, console, parser, controller_indices, controller_names) + contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console) if not contexts: console.print("[yellow]No controllers opened; waiting for hotplug events...[/yellow]") - - def ports_in_use() -> set: - used = set(mapping_by_index.values()) - used.update(ctx.port for ctx in contexts.values() if ctx.port) - return used - - def discover_new_ports() -> None: - if not auto_discover_ports: - return - discovered = discover_ports( - include_non_usb=include_non_usb, - ignore_descriptions=ignore_port_desc, - include_descriptions=include_port_desc, - ) - in_use = ports_in_use() - for info in discovered: - path = info["device"] - if path in in_use or path in available_ports: - continue - available_ports.append(path) - console.print(f"[green]Discovered UART {path} ({info['description']}); available for pairing.[/green]") - - def pair_waiting_contexts() -> None: - for ctx in list(contexts.values()): - if ctx.port is not None: - continue - port_choice = assign_port_for_index(ctx.controller_index) - if port_choice is None: - continue - ctx.port = port_choice - uart = open_uart_or_warn(port_choice, args.baud, console) - ctx.last_reopen_attempt = time.monotonic() - if uart: - uarts.append(uart) - ctx.uart = uart - console.print( - f"[green]Controller {ctx.controller_index} ({ctx.instance_id}) paired to {port_choice}[/green]" - ) - else: - ctx.uart = None - console.print( - f"[yellow]Controller {ctx.controller_index} ({ctx.instance_id}) waiting for UART {port_choice}[/yellow]" - ) - - event = sdl2.SDL_Event() - port_scan_interval = 2.0 - last_port_scan = time.monotonic() - running = True - while running: - while sdl2.SDL_PollEvent(event): - if event.type == sdl2.SDL_QUIT: - running = False - break - if event.type == sdl2.SDL_CONTROLLERAXISMOTION: - ctx = contexts.get(event.caxis.which) - if not ctx: - continue - axis = event.caxis.axis - value = event.caxis.value - if axis == sdl2.SDL_CONTROLLER_AXIS_LEFTX: - ctx.report.lx = axis_to_stick(value, deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_LEFTY: - ctx.report.ly = axis_to_stick(value, deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTX: - ctx.report.rx = axis_to_stick(value, deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTY: - ctx.report.ry = axis_to_stick(value, deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERLEFT: - pressed = trigger_to_button(value, trigger_threshold) - if pressed != ctx.last_trigger_state["left"]: - if pressed: - ctx.report.buttons |= SwitchButton.ZL - else: - ctx.report.buttons &= ~SwitchButton.ZL - ctx.last_trigger_state["left"] = pressed - elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERRIGHT: - pressed = trigger_to_button(value, trigger_threshold) - if pressed != ctx.last_trigger_state["right"]: - if pressed: - ctx.report.buttons |= SwitchButton.ZR - else: - ctx.report.buttons &= ~SwitchButton.ZR - ctx.last_trigger_state["right"] = pressed - elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): - ctx = contexts.get(event.cbutton.which) - if not ctx: - continue - current_button_map = ( - button_map_swapped - if (args.swap_abxy or ctx.controller_index in swap_abxy_indices) - else button_map_default - ) - button = event.cbutton.button - pressed = event.type == sdl2.SDL_CONTROLLERBUTTONDOWN - if button in current_button_map: - bit = current_button_map[button] - if pressed: - ctx.report.buttons |= bit - else: - ctx.report.buttons &= ~bit - ctx.button_state[button] = pressed - elif button in DPAD_BUTTONS: - ctx.dpad[DPAD_BUTTONS[button]] = pressed - ctx.report.hat = dpad_to_hat(ctx.dpad) - elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: - idx = event.cdevice.which - if any(c.controller_index == idx for c in contexts.values()): - continue - port = assign_port_for_index(idx) - if port is None and not auto_pairing_enabled: - continue - if idx >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(idx): - name = sdl2.SDL_JoystickNameForIndex(idx) - name_str = name.decode() if isinstance(name, bytes) else str(name) - console.print( - f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" - ) - continue - try: - controller, instance_id = open_controller(idx) - except Exception as exc: - console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") - continue - uart = open_uart_or_warn(port, args.baud, console) if port else None - if uart: - uarts.append(uart) - console.print(f"[green]Controller {idx} ({instance_id}) paired to {port}[/green]") - elif port: - console.print(f"[yellow]Controller {idx} ({instance_id}) waiting for UART {port}[/yellow]") - else: - console.print(f"[yellow]Controller {idx} ({instance_id}) connected; waiting for an available UART[/yellow]") - ctx = ControllerContext( - controller=controller, - instance_id=instance_id, - controller_index=idx, - port=port, - uart=uart, - ) - contexts[instance_id] = ctx - elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: - instance_id = event.cdevice.which - ctx = contexts.pop(instance_id, None) - if ctx: - console.print(f"[yellow]Controller {instance_id} removed[/yellow]") - if ctx.controller_index in auto_assigned_indices: - freed = mapping_by_index.pop(ctx.controller_index, None) - auto_assigned_indices.discard(ctx.controller_index) - if freed and freed not in available_ports: - available_ports.append(freed) - console.print(f"[cyan]Released UART {freed} back to pool[/cyan]") - sdl2.SDL_GameControllerClose(ctx.controller) - - now = time.monotonic() - if now - last_port_scan > port_scan_interval: - discover_new_ports() - last_port_scan = now - pair_waiting_contexts() - else: - pair_waiting_contexts() - for ctx in list(contexts.values()): - current_button_map = ( - button_map_swapped - if (args.swap_abxy or ctx.controller_index in swap_abxy_indices) - else button_map_default - ) - poll_controller_buttons(ctx, current_button_map) - # Reconnect UART if needed. - if ctx.port and ctx.uart is None and (now - ctx.last_reopen_attempt) > 1.0: - ctx.last_reopen_attempt = now - uart = open_uart_or_warn(ctx.port, args.baud, console) - if uart: - uarts.append(uart) - console.print(f"[green]Reconnected UART {ctx.port} for controller {ctx.controller_index}[/green]") - ctx.uart = uart - if ctx.uart is None: - continue - try: - # Send controller state at configured frequency - if now - ctx.last_send >= interval: - ctx.uart.send_report(ctx.report) - ctx.last_send = now - - # Drain all pending rumble frames and apply only the latest one. - last_payload = None - while True: - p = ctx.uart.read_rumble_payload() - if not p: - break - last_payload = p - - if last_payload is not None: - energy = apply_rumble(ctx.controller, last_payload) - ctx.rumble_active = energy >= RUMBLE_MIN_ACTIVE - if ctx.rumble_active and energy != ctx.last_rumble_energy: - ctx.last_rumble_change = now - ctx.last_rumble_energy = energy - ctx.last_rumble = now - elif ctx.rumble_active and (now - ctx.last_rumble) > RUMBLE_IDLE_TIMEOUT: - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) - ctx.rumble_active = False - ctx.last_rumble_energy = 0.0 - elif ctx.rumble_active and (now - ctx.last_rumble_change) > RUMBLE_STUCK_TIMEOUT: - # Guard against a stream of tiny-but-nonzero rumble packets that never decay. - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) - ctx.rumble_active = False - ctx.last_rumble_energy = 0.0 - except SerialException as exc: - console.print(f"[yellow]UART {ctx.port} disconnected: {exc}[/yellow]") - try: - ctx.uart.close() - except Exception: - pass - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) - ctx.uart = None - ctx.rumble_active = False - ctx.last_rumble_energy = 0.0 - ctx.last_reopen_attempt = now - except Exception as exc: - console.print(f"[red]UART error on {ctx.port}: {exc}[/red]") - sdl2.SDL_Delay(1) + run_bridge_loop(args, console, config, pairing, contexts, uarts) finally: - for ctx in contexts.values(): - sdl2.SDL_GameControllerClose(ctx.controller) - for uart in uarts: - uart.close() - sdl2.SDL_Quit() + cleanup(contexts, uarts) if __name__ == "__main__": From 4b7dbc4fbd2e301088375ad51af7c78b41bf33d6 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 23 Nov 2025 09:31:43 -0700 Subject: [PATCH 043/134] Comments --- controller_uart_bridge.py | 43 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 81f77da..f3acf53 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -75,6 +75,7 @@ class SwitchHat: def parse_mapping(value: str) -> Tuple[int, str]: + """Parse 'index:serial_port' CLI mapping argument.""" if ":" not in value: raise argparse.ArgumentTypeError("Mapping must look like 'index:serial_port'") idx_str, port = value.split(":", 1) @@ -88,6 +89,7 @@ def parse_mapping(value: str) -> Tuple[int, str]: def axis_to_stick(value: int, deadzone: int) -> int: + """Convert a signed SDL axis value to 0-255 stick range with deadzone.""" if abs(value) < deadzone: value = 0 scaled = int((value + 32768) * 255 / 65535) @@ -95,6 +97,7 @@ def axis_to_stick(value: int, deadzone: int) -> int: def trigger_to_button(value: int, threshold: int) -> bool: + """Return True if analog trigger crosses digital threshold.""" return value >= threshold @@ -131,6 +134,7 @@ DPAD_BUTTONS = { def dpad_to_hat(flags: Dict[str, bool]) -> int: + """Translate DPAD button flags into a Switch hat value.""" up = flags["up"] down = flags["down"] left = flags["left"] @@ -156,6 +160,7 @@ def dpad_to_hat(flags: Dict[str, bool]) -> int: def is_usb_serial(path: str) -> bool: + """Heuristic for USB serial path prefixes.""" if path.startswith("/dev/tty.") and not path.startswith("/dev/tty.usb"): return False if path.startswith("/dev/cu.") and not path.startswith("/dev/cu.usb"): @@ -179,6 +184,7 @@ def discover_ports( ignore_descriptions: Optional[List[str]] = None, include_descriptions: Optional[List[str]] = None, ) -> List[Dict[str, str]]: + """List serial ports, optionally filtering by description and USB-ness.""" ignored = [d.lower() for d in ignore_descriptions or []] includes = [d.lower() for d in include_descriptions or []] results: List[Dict[str, str]] = [] @@ -205,6 +211,7 @@ def discover_ports( def interactive_pairing( console: Console, controller_info: Dict[int, str], ports: List[Dict[str, str]] ) -> List[Tuple[int, str]]: + """Prompt the user to pair controllers to UART ports via Rich UI.""" available = ports.copy() mappings: List[Tuple[int, str]] = [] for controller_idx in controller_info: @@ -246,6 +253,7 @@ class SwitchReport: ry: int = 128 def to_bytes(self) -> bytes: + """Serialize the report into the UART packet format.""" return struct.pack( " None: + """Open a UART connection to the Pico with non-blocking IO.""" self.serial = serial.Serial( port=port, baudrate=baudrate, @@ -268,6 +277,7 @@ class PicoUART: self._buffer = bytearray() def send_report(self, report: SwitchReport) -> None: + """Send a controller report to the Pico.""" # Non-blocking write; no flush to avoid sync stalls. self.serial.write(report.to_bytes()) @@ -311,10 +321,11 @@ class PicoUART: del self._buffer[:start + 11] return payload - # Bad frame, drop this header and resync + # Bad frame, drop this header and resync to the next candidate del self._buffer[:start + 1] def close(self) -> None: + """Close the UART connection.""" self.serial.close() @@ -337,6 +348,7 @@ def decode_rumble(payload: bytes) -> Tuple[float, float]: def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> float: + """Apply rumble payload to SDL controller and return max normalized energy.""" left_norm, right_norm = decode_rumble(payload) max_norm = max(left_norm, right_norm) # Treat small rumble as "off" to avoid idle buzz. @@ -372,6 +384,7 @@ class ControllerContext: def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: + """Open an SDL GameController by index and return it with instance ID.""" controller = sdl2.SDL_GameControllerOpen(index) if not controller: raise RuntimeError(f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}") @@ -381,6 +394,7 @@ def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: + """Attempt to open a UART without logging; return None on failure.""" try: return PicoUART(port, baud) except Exception: @@ -388,6 +402,7 @@ def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: def open_uart_or_warn(port: str, baud: int, console: Console) -> Optional[PicoUART]: + """Open a UART and warn on failure.""" try: return PicoUART(port, baud) except Exception as exc: @@ -396,6 +411,7 @@ def open_uart_or_warn(port: str, baud: int, console: Console) -> Optional[PicoUA def build_arg_parser() -> argparse.ArgumentParser: + """Construct the CLI argument parser for the bridge.""" parser = argparse.ArgumentParser(description="Bridge SDL2 controllers to switch-pico UART (with rumble)") parser.add_argument( "--map", @@ -470,6 +486,7 @@ def build_arg_parser() -> argparse.ArgumentParser: def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, int]) -> None: + """Update button/hat state based on current SDL controller readings.""" changed = False for sdl_button, switch_bit in button_map.items(): pressed = bool(sdl2.SDL_GameControllerGetButton(ctx.controller, sdl_button)) @@ -541,6 +558,7 @@ def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[i def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeConfig: + """Derive bridge runtime configuration from CLI arguments.""" interval = 1.0 / max(args.frequency, 1.0) deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) @@ -556,6 +574,7 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon def initialize_sdl(parser: argparse.ArgumentParser) -> None: + """Set SDL hints and initialize subsystems needed for controllers.""" sdl2.SDL_SetHint(sdl2.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, b"1") set_hint("SDL_JOYSTICK_HIDAPI", "1") set_hint("SDL_JOYSTICK_HIDAPI_SWITCH", "1") @@ -568,6 +587,7 @@ def initialize_sdl(parser: argparse.ArgumentParser) -> None: def detect_controllers( console: Console, args: argparse.Namespace, parser: argparse.ArgumentParser ) -> Tuple[List[int], Dict[int, str]]: + """Detect available controllers and return usable indices and names.""" controller_indices: List[int] = [] controller_names: Dict[int, str] = {} controller_count = sdl2.SDL_NumJoysticks() @@ -601,6 +621,7 @@ def prepare_pairing_state( controller_indices: List[int], controller_names: Dict[int, str], ) -> PairingState: + """Prepare pairing preferences and pre-seeded mappings from CLI options.""" auto_pairing_enabled = not args.map and not args.interactive auto_discover_ports = auto_pairing_enabled and not args.ports include_non_usb = args.all_ports or False @@ -612,6 +633,7 @@ def prepare_pairing_state( if args.interactive: if not controller_indices: parser.error("No controllers detected for interactive pairing.") + # Interactive pairing shows the discovered ports and lets the user bind explicitly. discovered = discover_ports( include_non_usb=include_non_usb, ignore_descriptions=ignore_port_desc, @@ -627,6 +649,7 @@ def prepare_pairing_state( available_ports.extend(list(args.ports)) console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") else: + # Passive mode: grab whatever UARTs exist now, and keep looking later. discovered = discover_ports( include_non_usb=include_non_usb, ignore_descriptions=ignore_port_desc, @@ -653,6 +676,7 @@ def prepare_pairing_state( def assign_port_for_index(pairing: PairingState, idx: int, console: Console) -> Optional[str]: + """Return the UART assigned to a controller index, auto-pairing if allowed.""" if idx in pairing.mapping_by_index: return pairing.mapping_by_index[idx] if not pairing.auto_pairing_enabled: @@ -667,12 +691,14 @@ def assign_port_for_index(pairing: PairingState, idx: int, console: Console) -> def ports_in_use(pairing: PairingState, contexts: Dict[int, ControllerContext]) -> set: + """Return a set of UART paths currently reserved or mapped.""" used = set(pairing.mapping_by_index.values()) used.update(ctx.port for ctx in contexts.values() if ctx.port) return used def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console) -> None: + """Scan for new serial ports and add unused ones to the available pool.""" if not pairing.auto_discover_ports: return discovered = discover_ports( @@ -696,9 +722,11 @@ def pair_waiting_contexts( uarts: List[PicoUART], console: Console, ) -> None: + """Attach UARTs to contexts that are waiting for a port assignment/open.""" for ctx in list(contexts.values()): if ctx.port is not None: continue + # Try to grab a port for this controller; if none are available, leave it waiting. port_choice = assign_port_for_index(pairing, ctx.controller_index, console) if port_choice is None: continue @@ -717,6 +745,7 @@ def pair_waiting_contexts( def open_initial_contexts( args: argparse.Namespace, pairing: PairingState, controller_indices: List[int], console: Console ) -> Tuple[Dict[int, ControllerContext], List[PicoUART]]: + """Open initial controllers and UARTs for detected indices.""" contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] for index in controller_indices: @@ -753,6 +782,7 @@ def open_initial_contexts( def handle_axis_motion(event: sdl2.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig) -> None: + """Process axis motion event into stick/trigger state.""" ctx = contexts.get(event.caxis.which) if not ctx: return @@ -790,6 +820,7 @@ def handle_button_event( config: BridgeConfig, contexts: Dict[int, ControllerContext], ) -> None: + """Process button events into report/dpad state.""" ctx = contexts.get(event.cbutton.which) if not ctx: return @@ -820,7 +851,9 @@ def handle_device_added( uarts: List[PicoUART], console: Console, ) -> None: + """Handle controller hotplug by opening and pairing UART if possible.""" idx = event.cdevice.which + # If we already have a context for this logical index, ignore the duplicate event. if any(c.controller_index == idx for c in contexts.values()): return port = assign_port_for_index(pairing, idx, console) @@ -860,12 +893,14 @@ def handle_device_removed( contexts: Dict[int, ControllerContext], console: Console, ) -> None: + """Handle controller removal and release any auto-assigned UART.""" instance_id = event.cdevice.which ctx = contexts.pop(instance_id, None) if not ctx: return console.print(f"[yellow]Controller {instance_id} removed[/yellow]") if ctx.controller_index in pairing.auto_assigned_indices: + # Return auto-paired UART back to the pool so a future device can use it. freed = pairing.mapping_by_index.pop(ctx.controller_index, None) pairing.auto_assigned_indices.discard(ctx.controller_index) if freed and freed not in pairing.available_ports: @@ -882,6 +917,7 @@ def service_contexts( uarts: List[PicoUART], console: Console, ) -> None: + """Poll controllers, reconnect UARTs, send reports, and apply rumble.""" for ctx in list(contexts.values()): current_button_map = ( config.button_map_swapped @@ -912,6 +948,7 @@ def service_contexts( last_payload = p if last_payload is not None: + # Apply only the freshest rumble payload seen during this tick. energy = apply_rumble(ctx.controller, last_payload) ctx.rumble_active = energy >= RUMBLE_MIN_ACTIVE if ctx.rumble_active and energy != ctx.last_rumble_energy: @@ -949,6 +986,7 @@ def run_bridge_loop( contexts: Dict[int, ControllerContext], uarts: List[PicoUART], ) -> None: + """Main event loop for bridging controllers to UART and handling rumble.""" event = sdl2.SDL_Event() port_scan_interval = 2.0 last_port_scan = time.monotonic() @@ -970,6 +1008,7 @@ def run_bridge_loop( now = time.monotonic() if now - last_port_scan > port_scan_interval: + # Periodically rescan for new UARTs to auto-pair hotplugged devices. discover_new_ports(pairing, contexts, console) last_port_scan = now pair_waiting_contexts(args, pairing, contexts, uarts, console) @@ -980,6 +1019,7 @@ def run_bridge_loop( def cleanup(contexts: Dict[int, ControllerContext], uarts: List[PicoUART]) -> None: + """Gracefully close controllers, UARTs, and SDL subsystems.""" for ctx in contexts.values(): sdl2.SDL_GameControllerClose(ctx.controller) for uart in uarts: @@ -988,6 +1028,7 @@ def cleanup(contexts: Dict[int, ControllerContext], uarts: List[PicoUART]) -> No def main() -> None: + """Entry point: parse args, set up SDL, and run the bridge loop.""" parser = build_arg_parser() args = parser.parse_args() console = Console() From 78201e831653d334128691f5e56101b5fcd48cfd Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 23 Nov 2025 09:31:43 -0700 Subject: [PATCH 044/134] Comments --- controller_uart_bridge.py | 43 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 81f77da..f3acf53 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -75,6 +75,7 @@ class SwitchHat: def parse_mapping(value: str) -> Tuple[int, str]: + """Parse 'index:serial_port' CLI mapping argument.""" if ":" not in value: raise argparse.ArgumentTypeError("Mapping must look like 'index:serial_port'") idx_str, port = value.split(":", 1) @@ -88,6 +89,7 @@ def parse_mapping(value: str) -> Tuple[int, str]: def axis_to_stick(value: int, deadzone: int) -> int: + """Convert a signed SDL axis value to 0-255 stick range with deadzone.""" if abs(value) < deadzone: value = 0 scaled = int((value + 32768) * 255 / 65535) @@ -95,6 +97,7 @@ def axis_to_stick(value: int, deadzone: int) -> int: def trigger_to_button(value: int, threshold: int) -> bool: + """Return True if analog trigger crosses digital threshold.""" return value >= threshold @@ -131,6 +134,7 @@ DPAD_BUTTONS = { def dpad_to_hat(flags: Dict[str, bool]) -> int: + """Translate DPAD button flags into a Switch hat value.""" up = flags["up"] down = flags["down"] left = flags["left"] @@ -156,6 +160,7 @@ def dpad_to_hat(flags: Dict[str, bool]) -> int: def is_usb_serial(path: str) -> bool: + """Heuristic for USB serial path prefixes.""" if path.startswith("/dev/tty.") and not path.startswith("/dev/tty.usb"): return False if path.startswith("/dev/cu.") and not path.startswith("/dev/cu.usb"): @@ -179,6 +184,7 @@ def discover_ports( ignore_descriptions: Optional[List[str]] = None, include_descriptions: Optional[List[str]] = None, ) -> List[Dict[str, str]]: + """List serial ports, optionally filtering by description and USB-ness.""" ignored = [d.lower() for d in ignore_descriptions or []] includes = [d.lower() for d in include_descriptions or []] results: List[Dict[str, str]] = [] @@ -205,6 +211,7 @@ def discover_ports( def interactive_pairing( console: Console, controller_info: Dict[int, str], ports: List[Dict[str, str]] ) -> List[Tuple[int, str]]: + """Prompt the user to pair controllers to UART ports via Rich UI.""" available = ports.copy() mappings: List[Tuple[int, str]] = [] for controller_idx in controller_info: @@ -246,6 +253,7 @@ class SwitchReport: ry: int = 128 def to_bytes(self) -> bytes: + """Serialize the report into the UART packet format.""" return struct.pack( " None: + """Open a UART connection to the Pico with non-blocking IO.""" self.serial = serial.Serial( port=port, baudrate=baudrate, @@ -268,6 +277,7 @@ class PicoUART: self._buffer = bytearray() def send_report(self, report: SwitchReport) -> None: + """Send a controller report to the Pico.""" # Non-blocking write; no flush to avoid sync stalls. self.serial.write(report.to_bytes()) @@ -311,10 +321,11 @@ class PicoUART: del self._buffer[:start + 11] return payload - # Bad frame, drop this header and resync + # Bad frame, drop this header and resync to the next candidate del self._buffer[:start + 1] def close(self) -> None: + """Close the UART connection.""" self.serial.close() @@ -337,6 +348,7 @@ def decode_rumble(payload: bytes) -> Tuple[float, float]: def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> float: + """Apply rumble payload to SDL controller and return max normalized energy.""" left_norm, right_norm = decode_rumble(payload) max_norm = max(left_norm, right_norm) # Treat small rumble as "off" to avoid idle buzz. @@ -372,6 +384,7 @@ class ControllerContext: def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: + """Open an SDL GameController by index and return it with instance ID.""" controller = sdl2.SDL_GameControllerOpen(index) if not controller: raise RuntimeError(f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}") @@ -381,6 +394,7 @@ def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: + """Attempt to open a UART without logging; return None on failure.""" try: return PicoUART(port, baud) except Exception: @@ -388,6 +402,7 @@ def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: def open_uart_or_warn(port: str, baud: int, console: Console) -> Optional[PicoUART]: + """Open a UART and warn on failure.""" try: return PicoUART(port, baud) except Exception as exc: @@ -396,6 +411,7 @@ def open_uart_or_warn(port: str, baud: int, console: Console) -> Optional[PicoUA def build_arg_parser() -> argparse.ArgumentParser: + """Construct the CLI argument parser for the bridge.""" parser = argparse.ArgumentParser(description="Bridge SDL2 controllers to switch-pico UART (with rumble)") parser.add_argument( "--map", @@ -470,6 +486,7 @@ def build_arg_parser() -> argparse.ArgumentParser: def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, int]) -> None: + """Update button/hat state based on current SDL controller readings.""" changed = False for sdl_button, switch_bit in button_map.items(): pressed = bool(sdl2.SDL_GameControllerGetButton(ctx.controller, sdl_button)) @@ -541,6 +558,7 @@ def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[i def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeConfig: + """Derive bridge runtime configuration from CLI arguments.""" interval = 1.0 / max(args.frequency, 1.0) deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) @@ -556,6 +574,7 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon def initialize_sdl(parser: argparse.ArgumentParser) -> None: + """Set SDL hints and initialize subsystems needed for controllers.""" sdl2.SDL_SetHint(sdl2.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, b"1") set_hint("SDL_JOYSTICK_HIDAPI", "1") set_hint("SDL_JOYSTICK_HIDAPI_SWITCH", "1") @@ -568,6 +587,7 @@ def initialize_sdl(parser: argparse.ArgumentParser) -> None: def detect_controllers( console: Console, args: argparse.Namespace, parser: argparse.ArgumentParser ) -> Tuple[List[int], Dict[int, str]]: + """Detect available controllers and return usable indices and names.""" controller_indices: List[int] = [] controller_names: Dict[int, str] = {} controller_count = sdl2.SDL_NumJoysticks() @@ -601,6 +621,7 @@ def prepare_pairing_state( controller_indices: List[int], controller_names: Dict[int, str], ) -> PairingState: + """Prepare pairing preferences and pre-seeded mappings from CLI options.""" auto_pairing_enabled = not args.map and not args.interactive auto_discover_ports = auto_pairing_enabled and not args.ports include_non_usb = args.all_ports or False @@ -612,6 +633,7 @@ def prepare_pairing_state( if args.interactive: if not controller_indices: parser.error("No controllers detected for interactive pairing.") + # Interactive pairing shows the discovered ports and lets the user bind explicitly. discovered = discover_ports( include_non_usb=include_non_usb, ignore_descriptions=ignore_port_desc, @@ -627,6 +649,7 @@ def prepare_pairing_state( available_ports.extend(list(args.ports)) console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") else: + # Passive mode: grab whatever UARTs exist now, and keep looking later. discovered = discover_ports( include_non_usb=include_non_usb, ignore_descriptions=ignore_port_desc, @@ -653,6 +676,7 @@ def prepare_pairing_state( def assign_port_for_index(pairing: PairingState, idx: int, console: Console) -> Optional[str]: + """Return the UART assigned to a controller index, auto-pairing if allowed.""" if idx in pairing.mapping_by_index: return pairing.mapping_by_index[idx] if not pairing.auto_pairing_enabled: @@ -667,12 +691,14 @@ def assign_port_for_index(pairing: PairingState, idx: int, console: Console) -> def ports_in_use(pairing: PairingState, contexts: Dict[int, ControllerContext]) -> set: + """Return a set of UART paths currently reserved or mapped.""" used = set(pairing.mapping_by_index.values()) used.update(ctx.port for ctx in contexts.values() if ctx.port) return used def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console) -> None: + """Scan for new serial ports and add unused ones to the available pool.""" if not pairing.auto_discover_ports: return discovered = discover_ports( @@ -696,9 +722,11 @@ def pair_waiting_contexts( uarts: List[PicoUART], console: Console, ) -> None: + """Attach UARTs to contexts that are waiting for a port assignment/open.""" for ctx in list(contexts.values()): if ctx.port is not None: continue + # Try to grab a port for this controller; if none are available, leave it waiting. port_choice = assign_port_for_index(pairing, ctx.controller_index, console) if port_choice is None: continue @@ -717,6 +745,7 @@ def pair_waiting_contexts( def open_initial_contexts( args: argparse.Namespace, pairing: PairingState, controller_indices: List[int], console: Console ) -> Tuple[Dict[int, ControllerContext], List[PicoUART]]: + """Open initial controllers and UARTs for detected indices.""" contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] for index in controller_indices: @@ -753,6 +782,7 @@ def open_initial_contexts( def handle_axis_motion(event: sdl2.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig) -> None: + """Process axis motion event into stick/trigger state.""" ctx = contexts.get(event.caxis.which) if not ctx: return @@ -790,6 +820,7 @@ def handle_button_event( config: BridgeConfig, contexts: Dict[int, ControllerContext], ) -> None: + """Process button events into report/dpad state.""" ctx = contexts.get(event.cbutton.which) if not ctx: return @@ -820,7 +851,9 @@ def handle_device_added( uarts: List[PicoUART], console: Console, ) -> None: + """Handle controller hotplug by opening and pairing UART if possible.""" idx = event.cdevice.which + # If we already have a context for this logical index, ignore the duplicate event. if any(c.controller_index == idx for c in contexts.values()): return port = assign_port_for_index(pairing, idx, console) @@ -860,12 +893,14 @@ def handle_device_removed( contexts: Dict[int, ControllerContext], console: Console, ) -> None: + """Handle controller removal and release any auto-assigned UART.""" instance_id = event.cdevice.which ctx = contexts.pop(instance_id, None) if not ctx: return console.print(f"[yellow]Controller {instance_id} removed[/yellow]") if ctx.controller_index in pairing.auto_assigned_indices: + # Return auto-paired UART back to the pool so a future device can use it. freed = pairing.mapping_by_index.pop(ctx.controller_index, None) pairing.auto_assigned_indices.discard(ctx.controller_index) if freed and freed not in pairing.available_ports: @@ -882,6 +917,7 @@ def service_contexts( uarts: List[PicoUART], console: Console, ) -> None: + """Poll controllers, reconnect UARTs, send reports, and apply rumble.""" for ctx in list(contexts.values()): current_button_map = ( config.button_map_swapped @@ -912,6 +948,7 @@ def service_contexts( last_payload = p if last_payload is not None: + # Apply only the freshest rumble payload seen during this tick. energy = apply_rumble(ctx.controller, last_payload) ctx.rumble_active = energy >= RUMBLE_MIN_ACTIVE if ctx.rumble_active and energy != ctx.last_rumble_energy: @@ -949,6 +986,7 @@ def run_bridge_loop( contexts: Dict[int, ControllerContext], uarts: List[PicoUART], ) -> None: + """Main event loop for bridging controllers to UART and handling rumble.""" event = sdl2.SDL_Event() port_scan_interval = 2.0 last_port_scan = time.monotonic() @@ -970,6 +1008,7 @@ def run_bridge_loop( now = time.monotonic() if now - last_port_scan > port_scan_interval: + # Periodically rescan for new UARTs to auto-pair hotplugged devices. discover_new_ports(pairing, contexts, console) last_port_scan = now pair_waiting_contexts(args, pairing, contexts, uarts, console) @@ -980,6 +1019,7 @@ def run_bridge_loop( def cleanup(contexts: Dict[int, ControllerContext], uarts: List[PicoUART]) -> None: + """Gracefully close controllers, UARTs, and SDL subsystems.""" for ctx in contexts.values(): sdl2.SDL_GameControllerClose(ctx.controller) for uart in uarts: @@ -988,6 +1028,7 @@ def cleanup(contexts: Dict[int, ControllerContext], uarts: List[PicoUART]) -> No def main() -> None: + """Entry point: parse args, set up SDL, and run the bridge loop.""" parser = build_arg_parser() args = parser.parse_args() console = Console() From dca3e0f9d1ecd030ccf72e8642c512eba1d18011 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 23 Nov 2025 09:33:08 -0700 Subject: [PATCH 045/134] README update --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c25bc29..4845dc1 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a - **TX**: GPIO4 (Pico pin 6) → RX of your USB‑serial adapter. - **RX**: GPIO5 (Pico pin 7) → TX of your USB‑serial adapter. - **GND**: common ground between Pico and adapter. -- Baud rate: **1,000,000** (default). Some adapters only handle 500,000; both bridges accept a `--baud` flag. +- Baud rate: **921600** (default). Some adapters only handle 500,000; both bridges accept a `--baud` flag. - Keep logic at 3.3V; do not feed 5V UART into the Pico. ## Building and flashing firmware @@ -49,7 +49,7 @@ python controller_uart_bridge.py --interactive Options: - `--map index:PORT` (repeatable) to pin controller index to serial (e.g., `--map 0:/dev/cu.usbserial-0001` or `--map 0:COM5`). - `--ports PORTS...` or `--interactive` for auto/interactive pairing. -- `--baud 900000` (default 900,000; use `500000` if your adapter can’t do 900K). +- `--baud 921600` (default 921600; use `500000` if your adapter can’t do 900K). - `--frequency 1000` to send at 1 kHz. - `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). @@ -71,7 +71,6 @@ Note: Rust toolchain not currently present in this setup. If you install Rust + ## Troubleshooting - **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, Switch sees a “Pro Controller”. -- **Rumble lag**: increase baud to 900,000 (default) or 1,000,000 if your adapter supports it; otherwise increase `--frequency`. - **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `controller_uart_bridge.py` if needed. - **Guide/Home triggers system menu (macOS)**: try different controller mode (XInput/DInput), disable Steam overlay/controller support, or connect wired. - **SDL can’t see controller**: load `controller_db/gamecontrollerdb.txt` (default), add your own mapping, or try a different mode on the pad (e.g., XInput). From 38c9550359d6660f85b8cc4340857bb55f1192f0 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 23 Nov 2025 09:33:08 -0700 Subject: [PATCH 046/134] README update --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c25bc29..4845dc1 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a - **TX**: GPIO4 (Pico pin 6) → RX of your USB‑serial adapter. - **RX**: GPIO5 (Pico pin 7) → TX of your USB‑serial adapter. - **GND**: common ground between Pico and adapter. -- Baud rate: **1,000,000** (default). Some adapters only handle 500,000; both bridges accept a `--baud` flag. +- Baud rate: **921600** (default). Some adapters only handle 500,000; both bridges accept a `--baud` flag. - Keep logic at 3.3V; do not feed 5V UART into the Pico. ## Building and flashing firmware @@ -49,7 +49,7 @@ python controller_uart_bridge.py --interactive Options: - `--map index:PORT` (repeatable) to pin controller index to serial (e.g., `--map 0:/dev/cu.usbserial-0001` or `--map 0:COM5`). - `--ports PORTS...` or `--interactive` for auto/interactive pairing. -- `--baud 900000` (default 900,000; use `500000` if your adapter can’t do 900K). +- `--baud 921600` (default 921600; use `500000` if your adapter can’t do 900K). - `--frequency 1000` to send at 1 kHz. - `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). @@ -71,7 +71,6 @@ Note: Rust toolchain not currently present in this setup. If you install Rust + ## Troubleshooting - **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, Switch sees a “Pro Controller”. -- **Rumble lag**: increase baud to 900,000 (default) or 1,000,000 if your adapter supports it; otherwise increase `--frequency`. - **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `controller_uart_bridge.py` if needed. - **Guide/Home triggers system menu (macOS)**: try different controller mode (XInput/DInput), disable Steam overlay/controller support, or connect wired. - **SDL can’t see controller**: load `controller_db/gamecontrollerdb.txt` (default), add your own mapping, or try a different mode on the pad (e.g., XInput). From 954b47253c8aa53cefe9870b556ab0746504089e Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 23 Nov 2025 09:48:30 -0700 Subject: [PATCH 047/134] Fix hotplugging --- controller_uart_bridge.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index f3acf53..e359fe9 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -697,6 +697,32 @@ def ports_in_use(pairing: PairingState, contexts: Dict[int, ControllerContext]) return used +def handle_removed_port(path: str, pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console) -> None: + """Clear mappings/contexts for a UART path that disappeared.""" + if path in pairing.available_ports: + pairing.available_ports.remove(path) + console.print(f"[yellow]UART {path} removed; dropping from available pool[/yellow]") + indices_to_clear = [idx for idx, mapped in pairing.mapping_by_index.items() if mapped == path] + for idx in indices_to_clear: + pairing.mapping_by_index.pop(idx, None) + pairing.auto_assigned_indices.discard(idx) + for ctx in list(contexts.values()): + if ctx.port != path: + continue + if ctx.uart: + try: + ctx.uart.close() + except Exception: + pass + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + ctx.uart = None + ctx.port = None + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 + ctx.last_reopen_attempt = time.monotonic() + console.print(f"[yellow]UART {path} removed; controller {ctx.controller_index} waiting for reassignment[/yellow]") + + def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console) -> None: """Scan for new serial ports and add unused ones to the available pool.""" if not pairing.auto_discover_ports: @@ -706,6 +732,14 @@ def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerCont ignore_descriptions=pairing.ignore_port_desc, include_descriptions=pairing.include_port_desc, ) + current_paths = {info["device"] for info in discovered} + known_paths = set(pairing.available_ports) + known_paths.update(pairing.mapping_by_index.values()) + known_paths.update(ctx.port for ctx in contexts.values() if ctx.port) + # Drop any paths we previously knew about that are no longer present. + removed_paths = [path for path in known_paths if path not in current_paths] + for path in removed_paths: + handle_removed_port(path, pairing, contexts, console) in_use = ports_in_use(pairing, contexts) for info in discovered: path = info["device"] From acdd702647e2972574c4d6e9e6468f217dfbb754 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 23 Nov 2025 09:48:30 -0700 Subject: [PATCH 048/134] Fix hotplugging --- controller_uart_bridge.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index f3acf53..e359fe9 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -697,6 +697,32 @@ def ports_in_use(pairing: PairingState, contexts: Dict[int, ControllerContext]) return used +def handle_removed_port(path: str, pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console) -> None: + """Clear mappings/contexts for a UART path that disappeared.""" + if path in pairing.available_ports: + pairing.available_ports.remove(path) + console.print(f"[yellow]UART {path} removed; dropping from available pool[/yellow]") + indices_to_clear = [idx for idx, mapped in pairing.mapping_by_index.items() if mapped == path] + for idx in indices_to_clear: + pairing.mapping_by_index.pop(idx, None) + pairing.auto_assigned_indices.discard(idx) + for ctx in list(contexts.values()): + if ctx.port != path: + continue + if ctx.uart: + try: + ctx.uart.close() + except Exception: + pass + sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + ctx.uart = None + ctx.port = None + ctx.rumble_active = False + ctx.last_rumble_energy = 0.0 + ctx.last_reopen_attempt = time.monotonic() + console.print(f"[yellow]UART {path} removed; controller {ctx.controller_index} waiting for reassignment[/yellow]") + + def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console) -> None: """Scan for new serial ports and add unused ones to the available pool.""" if not pairing.auto_discover_ports: @@ -706,6 +732,14 @@ def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerCont ignore_descriptions=pairing.ignore_port_desc, include_descriptions=pairing.include_port_desc, ) + current_paths = {info["device"] for info in discovered} + known_paths = set(pairing.available_ports) + known_paths.update(pairing.mapping_by_index.values()) + known_paths.update(ctx.port for ctx in contexts.values() if ctx.port) + # Drop any paths we previously knew about that are no longer present. + removed_paths = [path for path in known_paths if path not in current_paths] + for path in removed_paths: + handle_removed_port(path, pairing, contexts, console) in_use = ports_in_use(pairing, contexts) for info in discovered: path = info["device"] From 215e7f8bde9bf12267b373697a1996f1296ac9f7 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 23 Nov 2025 09:58:07 -0700 Subject: [PATCH 049/134] Docs and connection --- README.md | 6 ++++ controller_uart_bridge.py | 74 +++++++++++++++++++++++++++++---------- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 4845dc1..c4fd775 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,14 @@ python controller_uart_bridge.py --interactive Options: - `--map index:PORT` (repeatable) to pin controller index to serial (e.g., `--map 0:/dev/cu.usbserial-0001` or `--map 0:COM5`). - `--ports PORTS...` or `--interactive` for auto/interactive pairing. +- `--all-ports` to include non-USB serial devices in discovery. +- `--ignore-port-desc SUBSTR` / `--include-port-desc SUBSTR` to filter serial ports by description (repeatable). +- `--include-controller-name SUBSTR` to only open controllers whose name matches (repeatable). - `--baud 921600` (default 921600; use `500000` if your adapter can’t do 900K). - `--frequency 1000` to send at 1 kHz. +- `--deadzone 0.08` to change stick deadzone (0.0-1.0). +- `--trigger-threshold 0.35` to change analog trigger press threshold (0.0-1.0). +- `--swap-abxy` or `--swap-abxy-index N` to flip AB/XY globally or for specific controller indices (repeatable). - `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). Hot‑plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index e359fe9..1a7f9b7 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -20,6 +20,7 @@ import struct import threading import time from dataclasses import dataclass, field +from ctypes import create_string_buffer from pathlib import Path from typing import Dict, List, Optional, Tuple @@ -369,6 +370,7 @@ class ControllerContext: controller: sdl2.SDL_GameController instance_id: int controller_index: int + stable_id: int port: Optional[str] uart: Optional[PicoUART] report: SwitchReport = field(default_factory=SwitchReport) @@ -383,14 +385,18 @@ class ControllerContext: rumble_active: bool = False -def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: - """Open an SDL GameController by index and return it with instance ID.""" +def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int, str]: + """Open an SDL GameController by index and return it with instance ID and GUID string.""" controller = sdl2.SDL_GameControllerOpen(index) if not controller: raise RuntimeError(f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}") joystick = sdl2.SDL_GameControllerGetJoystick(controller) instance_id = sdl2.SDL_JoystickInstanceID(joystick) - return controller, instance_id + guid = sdl2.SDL_JoystickGetGUID(joystick) + buf = create_string_buffer(33) + sdl2.SDL_JoystickGetGUIDString(guid, buf, 33) + guid_str = buf.value.decode() if buf.value else "" + return controller, instance_id, guid_str def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: @@ -534,6 +540,19 @@ class PairingState: include_port_desc: List[str] = field(default_factory=list) +@dataclass +class ControllerIdRegistry: + """Assign stable IDs to controllers based on their GUID.""" + guid_to_id: Dict[str, int] = field(default_factory=dict) + next_id: int = 0 + + def stable_id_for_guid(self, guid: str) -> int: + if guid not in self.guid_to_id: + self.guid_to_id[guid] = self.next_id + self.next_id += 1 + return self.guid_to_id[guid] + + def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, int], Dict[int, int], set[int]]: """Load SDL controller mappings and return button map variants.""" default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" @@ -770,14 +789,22 @@ def pair_waiting_contexts( if uart: uarts.append(uart) ctx.uart = uart - console.print(f"[green]Controller {ctx.controller_index} ({ctx.instance_id}) paired to {port_choice}[/green]") + console.print( + f"[green]Controller {ctx.controller_index} (id {ctx.stable_id}, inst {ctx.instance_id}) paired to {port_choice}[/green]" + ) else: ctx.uart = None - console.print(f"[yellow]Controller {ctx.controller_index} ({ctx.instance_id}) waiting for UART {port_choice}[/yellow]") + console.print( + f"[yellow]Controller {ctx.controller_index} (id {ctx.stable_id}, inst {ctx.instance_id}) waiting for UART {port_choice}[/yellow]" + ) def open_initial_contexts( - args: argparse.Namespace, pairing: PairingState, controller_indices: List[int], console: Console + args: argparse.Namespace, + pairing: PairingState, + controller_indices: List[int], + console: Console, + id_registry: ControllerIdRegistry, ) -> Tuple[Dict[int, ControllerContext], List[PicoUART]]: """Open initial controllers and UARTs for detected indices.""" contexts: Dict[int, ControllerContext] = {} @@ -792,22 +819,26 @@ def open_initial_contexts( if port is None and not pairing.auto_pairing_enabled: continue try: - controller, instance_id = open_controller(index) + controller, instance_id, guid = open_controller(index) except Exception as exc: console.print(f"[red]Failed to open controller {index}: {exc}[/red]") continue + stable_id = id_registry.stable_id_for_guid(guid) uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) - console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") + console.print(f"[green]Controller {index} (id {stable_id}, inst {instance_id}) paired to {port}[/green]") elif port: - console.print(f"[yellow]Controller {index} ({instance_id}) waiting for UART {port}[/yellow]") + console.print(f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]") else: - console.print(f"[yellow]Controller {index} ({instance_id}) connected; waiting for an available UART[/yellow]") + console.print( + f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" + ) ctx = ControllerContext( controller=controller, instance_id=instance_id, controller_index=index, + stable_id=stable_id, port=port, uart=uart, ) @@ -884,6 +915,7 @@ def handle_device_added( contexts: Dict[int, ControllerContext], uarts: List[PicoUART], console: Console, + id_registry: ControllerIdRegistry, ) -> None: """Handle controller hotplug by opening and pairing UART if possible.""" idx = event.cdevice.which @@ -899,22 +931,26 @@ def handle_device_added( console.print(f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") return try: - controller, instance_id = open_controller(idx) + controller, instance_id, guid = open_controller(idx) except Exception as exc: console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") return + stable_id = id_registry.stable_id_for_guid(guid) uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) - console.print(f"[green]Controller {idx} ({instance_id}) paired to {port}[/green]") + console.print(f"[green]Controller {idx} (id {stable_id}, inst {instance_id}) paired to {port}[/green]") elif port: - console.print(f"[yellow]Controller {idx} ({instance_id}) waiting for UART {port}[/yellow]") + console.print(f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]") else: - console.print(f"[yellow]Controller {idx} ({instance_id}) connected; waiting for an available UART[/yellow]") + console.print( + f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" + ) ctx = ControllerContext( controller=controller, instance_id=instance_id, controller_index=idx, + stable_id=stable_id, port=port, uart=uart, ) @@ -932,7 +968,7 @@ def handle_device_removed( ctx = contexts.pop(instance_id, None) if not ctx: return - console.print(f"[yellow]Controller {instance_id} removed[/yellow]") + console.print(f"[yellow]Controller {instance_id} (id {ctx.stable_id}) removed[/yellow]") if ctx.controller_index in pairing.auto_assigned_indices: # Return auto-paired UART back to the pool so a future device can use it. freed = pairing.mapping_by_index.pop(ctx.controller_index, None) @@ -1019,6 +1055,7 @@ def run_bridge_loop( pairing: PairingState, contexts: Dict[int, ControllerContext], uarts: List[PicoUART], + id_registry: ControllerIdRegistry, ) -> None: """Main event loop for bridging controllers to UART and handling rumble.""" event = sdl2.SDL_Event() @@ -1036,7 +1073,7 @@ def run_bridge_loop( elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): handle_button_event(event, args, config, contexts) elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: - handle_device_added(event, args, pairing, contexts, uarts, console) + handle_device_added(event, args, pairing, contexts, uarts, console, id_registry) elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: handle_device_removed(event, pairing, contexts, console) @@ -1068,15 +1105,16 @@ def main() -> None: console = Console() config = build_bridge_config(console, args) initialize_sdl(parser) + id_registry = ControllerIdRegistry() contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] try: controller_indices, controller_names = detect_controllers(console, args, parser) pairing = prepare_pairing_state(args, console, parser, controller_indices, controller_names) - contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console) + contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console, id_registry) if not contexts: console.print("[yellow]No controllers opened; waiting for hotplug events...[/yellow]") - run_bridge_loop(args, console, config, pairing, contexts, uarts) + run_bridge_loop(args, console, config, pairing, contexts, uarts, id_registry) finally: cleanup(contexts, uarts) From 101fb1f3cb84af7aa482030ab57b1513263d7973 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 23 Nov 2025 09:58:07 -0700 Subject: [PATCH 050/134] Docs and connection --- README.md | 6 ++++ controller_uart_bridge.py | 74 +++++++++++++++++++++++++++++---------- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 4845dc1..c4fd775 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,14 @@ python controller_uart_bridge.py --interactive Options: - `--map index:PORT` (repeatable) to pin controller index to serial (e.g., `--map 0:/dev/cu.usbserial-0001` or `--map 0:COM5`). - `--ports PORTS...` or `--interactive` for auto/interactive pairing. +- `--all-ports` to include non-USB serial devices in discovery. +- `--ignore-port-desc SUBSTR` / `--include-port-desc SUBSTR` to filter serial ports by description (repeatable). +- `--include-controller-name SUBSTR` to only open controllers whose name matches (repeatable). - `--baud 921600` (default 921600; use `500000` if your adapter can’t do 900K). - `--frequency 1000` to send at 1 kHz. +- `--deadzone 0.08` to change stick deadzone (0.0-1.0). +- `--trigger-threshold 0.35` to change analog trigger press threshold (0.0-1.0). +- `--swap-abxy` or `--swap-abxy-index N` to flip AB/XY globally or for specific controller indices (repeatable). - `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). Hot‑plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index e359fe9..1a7f9b7 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -20,6 +20,7 @@ import struct import threading import time from dataclasses import dataclass, field +from ctypes import create_string_buffer from pathlib import Path from typing import Dict, List, Optional, Tuple @@ -369,6 +370,7 @@ class ControllerContext: controller: sdl2.SDL_GameController instance_id: int controller_index: int + stable_id: int port: Optional[str] uart: Optional[PicoUART] report: SwitchReport = field(default_factory=SwitchReport) @@ -383,14 +385,18 @@ class ControllerContext: rumble_active: bool = False -def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int]: - """Open an SDL GameController by index and return it with instance ID.""" +def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int, str]: + """Open an SDL GameController by index and return it with instance ID and GUID string.""" controller = sdl2.SDL_GameControllerOpen(index) if not controller: raise RuntimeError(f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}") joystick = sdl2.SDL_GameControllerGetJoystick(controller) instance_id = sdl2.SDL_JoystickInstanceID(joystick) - return controller, instance_id + guid = sdl2.SDL_JoystickGetGUID(joystick) + buf = create_string_buffer(33) + sdl2.SDL_JoystickGetGUIDString(guid, buf, 33) + guid_str = buf.value.decode() if buf.value else "" + return controller, instance_id, guid_str def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: @@ -534,6 +540,19 @@ class PairingState: include_port_desc: List[str] = field(default_factory=list) +@dataclass +class ControllerIdRegistry: + """Assign stable IDs to controllers based on their GUID.""" + guid_to_id: Dict[str, int] = field(default_factory=dict) + next_id: int = 0 + + def stable_id_for_guid(self, guid: str) -> int: + if guid not in self.guid_to_id: + self.guid_to_id[guid] = self.next_id + self.next_id += 1 + return self.guid_to_id[guid] + + def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, int], Dict[int, int], set[int]]: """Load SDL controller mappings and return button map variants.""" default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" @@ -770,14 +789,22 @@ def pair_waiting_contexts( if uart: uarts.append(uart) ctx.uart = uart - console.print(f"[green]Controller {ctx.controller_index} ({ctx.instance_id}) paired to {port_choice}[/green]") + console.print( + f"[green]Controller {ctx.controller_index} (id {ctx.stable_id}, inst {ctx.instance_id}) paired to {port_choice}[/green]" + ) else: ctx.uart = None - console.print(f"[yellow]Controller {ctx.controller_index} ({ctx.instance_id}) waiting for UART {port_choice}[/yellow]") + console.print( + f"[yellow]Controller {ctx.controller_index} (id {ctx.stable_id}, inst {ctx.instance_id}) waiting for UART {port_choice}[/yellow]" + ) def open_initial_contexts( - args: argparse.Namespace, pairing: PairingState, controller_indices: List[int], console: Console + args: argparse.Namespace, + pairing: PairingState, + controller_indices: List[int], + console: Console, + id_registry: ControllerIdRegistry, ) -> Tuple[Dict[int, ControllerContext], List[PicoUART]]: """Open initial controllers and UARTs for detected indices.""" contexts: Dict[int, ControllerContext] = {} @@ -792,22 +819,26 @@ def open_initial_contexts( if port is None and not pairing.auto_pairing_enabled: continue try: - controller, instance_id = open_controller(index) + controller, instance_id, guid = open_controller(index) except Exception as exc: console.print(f"[red]Failed to open controller {index}: {exc}[/red]") continue + stable_id = id_registry.stable_id_for_guid(guid) uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) - console.print(f"[green]Controller {index} ({instance_id}) paired to {port}[/green]") + console.print(f"[green]Controller {index} (id {stable_id}, inst {instance_id}) paired to {port}[/green]") elif port: - console.print(f"[yellow]Controller {index} ({instance_id}) waiting for UART {port}[/yellow]") + console.print(f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]") else: - console.print(f"[yellow]Controller {index} ({instance_id}) connected; waiting for an available UART[/yellow]") + console.print( + f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" + ) ctx = ControllerContext( controller=controller, instance_id=instance_id, controller_index=index, + stable_id=stable_id, port=port, uart=uart, ) @@ -884,6 +915,7 @@ def handle_device_added( contexts: Dict[int, ControllerContext], uarts: List[PicoUART], console: Console, + id_registry: ControllerIdRegistry, ) -> None: """Handle controller hotplug by opening and pairing UART if possible.""" idx = event.cdevice.which @@ -899,22 +931,26 @@ def handle_device_added( console.print(f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") return try: - controller, instance_id = open_controller(idx) + controller, instance_id, guid = open_controller(idx) except Exception as exc: console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") return + stable_id = id_registry.stable_id_for_guid(guid) uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) - console.print(f"[green]Controller {idx} ({instance_id}) paired to {port}[/green]") + console.print(f"[green]Controller {idx} (id {stable_id}, inst {instance_id}) paired to {port}[/green]") elif port: - console.print(f"[yellow]Controller {idx} ({instance_id}) waiting for UART {port}[/yellow]") + console.print(f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]") else: - console.print(f"[yellow]Controller {idx} ({instance_id}) connected; waiting for an available UART[/yellow]") + console.print( + f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" + ) ctx = ControllerContext( controller=controller, instance_id=instance_id, controller_index=idx, + stable_id=stable_id, port=port, uart=uart, ) @@ -932,7 +968,7 @@ def handle_device_removed( ctx = contexts.pop(instance_id, None) if not ctx: return - console.print(f"[yellow]Controller {instance_id} removed[/yellow]") + console.print(f"[yellow]Controller {instance_id} (id {ctx.stable_id}) removed[/yellow]") if ctx.controller_index in pairing.auto_assigned_indices: # Return auto-paired UART back to the pool so a future device can use it. freed = pairing.mapping_by_index.pop(ctx.controller_index, None) @@ -1019,6 +1055,7 @@ def run_bridge_loop( pairing: PairingState, contexts: Dict[int, ControllerContext], uarts: List[PicoUART], + id_registry: ControllerIdRegistry, ) -> None: """Main event loop for bridging controllers to UART and handling rumble.""" event = sdl2.SDL_Event() @@ -1036,7 +1073,7 @@ def run_bridge_loop( elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): handle_button_event(event, args, config, contexts) elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: - handle_device_added(event, args, pairing, contexts, uarts, console) + handle_device_added(event, args, pairing, contexts, uarts, console, id_registry) elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: handle_device_removed(event, pairing, contexts, console) @@ -1068,15 +1105,16 @@ def main() -> None: console = Console() config = build_bridge_config(console, args) initialize_sdl(parser) + id_registry = ControllerIdRegistry() contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] try: controller_indices, controller_names = detect_controllers(console, args, parser) pairing = prepare_pairing_state(args, console, parser, controller_indices, controller_names) - contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console) + contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console, id_registry) if not contexts: console.print("[yellow]No controllers opened; waiting for hotplug events...[/yellow]") - run_bridge_loop(args, console, config, pairing, contexts, uarts) + run_bridge_loop(args, console, config, pairing, contexts, uarts, id_registry) finally: cleanup(contexts, uarts) From 2835249614e5d63948d5d476c8c133e8fb83d1fc Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 23 Nov 2025 10:08:59 -0700 Subject: [PATCH 051/134] Add GUID based selection --- README.md | 5 +- controller_uart_bridge.py | 100 +++++++++++++++++++++++++++----------- 2 files changed, 75 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index c4fd775..a61aa02 100644 --- a/README.md +++ b/README.md @@ -52,11 +52,14 @@ Options: - `--all-ports` to include non-USB serial devices in discovery. - `--ignore-port-desc SUBSTR` / `--include-port-desc SUBSTR` to filter serial ports by description (repeatable). - `--include-controller-name SUBSTR` to only open controllers whose name matches (repeatable). +- `--list-controllers` to print detected controllers and their GUIDs, then exit (useful for GUID-based options). - `--baud 921600` (default 921600; use `500000` if your adapter can’t do 900K). - `--frequency 1000` to send at 1 kHz. - `--deadzone 0.08` to change stick deadzone (0.0-1.0). - `--trigger-threshold 0.35` to change analog trigger press threshold (0.0-1.0). -- `--swap-abxy` or `--swap-abxy-index N` to flip AB/XY globally or for specific controller indices (repeatable). +- `--swap-abxy` to flip AB/XY globally. +- `--swap-abxy-index N` (repeatable) to flip AB/XY for controllers first seen at index N (auto-converts to a stable GUID). +- `--swap-abxy-guid GUID` (repeatable) to flip AB/XY for a specific physical controller (GUID is stable across runs). - `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). Hot‑plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 1a7f9b7..453e9cb 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -370,7 +370,7 @@ class ControllerContext: controller: sdl2.SDL_GameController instance_id: int controller_index: int - stable_id: int + stable_id: str port: Optional[str] uart: Optional[PicoUART] report: SwitchReport = field(default_factory=SwitchReport) @@ -392,10 +392,7 @@ def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int, str]: raise RuntimeError(f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}") joystick = sdl2.SDL_GameControllerGetJoystick(controller) instance_id = sdl2.SDL_JoystickInstanceID(joystick) - guid = sdl2.SDL_JoystickGetGUID(joystick) - buf = create_string_buffer(33) - sdl2.SDL_JoystickGetGUIDString(guid, buf, 33) - guid_str = buf.value.decode() if buf.value else "" + guid_str = guid_string_from_joystick(joystick) return controller, instance_id, guid_str @@ -407,6 +404,22 @@ def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: return None +def guid_string_from_joystick(joystick: sdl2.SDL_Joystick) -> str: + """Return a GUID string for an already-open joystick.""" + guid = sdl2.SDL_JoystickGetGUID(joystick) + buf = create_string_buffer(33) + sdl2.SDL_JoystickGetGUIDString(guid, buf, 33) + return buf.value.decode().lower() if buf.value else "" + + +def guid_string_for_device_index(index: int) -> str: + """Return a GUID string for a joystick device index without opening it.""" + guid = sdl2.SDL_JoystickGetDeviceGUID(index) + buf = create_string_buffer(33) + sdl2.SDL_JoystickGetGUIDString(guid, buf, 33) + return buf.value.decode().lower() if buf.value else "" + + def open_uart_or_warn(port: str, baud: int, console: Console) -> Optional[PicoUART]: """Open a UART and warn on failure.""" try: @@ -470,6 +483,11 @@ def build_arg_parser() -> argparse.ArgumentParser: default=[], help="Only open controllers whose name contains this substring (case-insensitive). Repeatable.", ) + parser.add_argument( + "--list-controllers", + action="store_true", + help="List detected controllers with GUIDs and exit.", + ) parser.add_argument( "--swap-abxy", action="store_true", @@ -482,6 +500,12 @@ def build_arg_parser() -> argparse.ArgumentParser: default=[], help="Swap AB/XY mapping for specific controller indices (repeatable).", ) + parser.add_argument( + "--swap-abxy-guid", + action="append", + default=[], + help="Swap AB/XY mapping for specific controller GUIDs (see --list-controllers). Repeatable.", + ) parser.add_argument( "--sdl-mapping", action="append", @@ -526,6 +550,7 @@ class BridgeConfig: button_map_default: Dict[int, int] button_map_swapped: Dict[int, int] swap_abxy_indices: set[int] + swap_abxy_ids: set[str] @dataclass @@ -540,19 +565,6 @@ class PairingState: include_port_desc: List[str] = field(default_factory=list) -@dataclass -class ControllerIdRegistry: - """Assign stable IDs to controllers based on their GUID.""" - guid_to_id: Dict[str, int] = field(default_factory=dict) - next_id: int = 0 - - def stable_id_for_guid(self, guid: str) -> int: - if guid not in self.guid_to_id: - self.guid_to_id[guid] = self.next_id - self.next_id += 1 - return self.guid_to_id[guid] - - def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, int], Dict[int, int], set[int]]: """Load SDL controller mappings and return button map variants.""" default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" @@ -582,6 +594,7 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) button_map_default, button_map_swapped, swap_abxy_indices = load_button_maps(console, args) + swap_abxy_guids = {g.lower() for g in args.swap_abxy_guid} return BridgeConfig( interval=interval, deadzone_raw=deadzone_raw, @@ -589,6 +602,7 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon button_map_default=button_map_default, button_map_swapped=button_map_swapped, swap_abxy_indices=swap_abxy_indices, + swap_abxy_ids=set(swap_abxy_guids), # filled later once stable IDs are known ) @@ -633,6 +647,28 @@ def detect_controllers( return controller_indices, controller_names +def list_controllers_with_guids(console: Console, parser: argparse.ArgumentParser) -> None: + """Print detected controllers with their GUID strings and exit.""" + count = sdl2.SDL_NumJoysticks() + if count < 0: + parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") + if count == 0: + console.print("[yellow]No controllers detected.[/yellow]") + return + table = Table(title="Detected Controllers (GUIDs)") + table.add_column("Index", justify="center") + table.add_column("Type") + table.add_column("Name") + table.add_column("GUID") + for idx in range(count): + is_gc = sdl2.SDL_IsGameController(idx) + name = sdl2.SDL_GameControllerNameForIndex(idx) if is_gc else sdl2.SDL_JoystickNameForIndex(idx) + name_str = name.decode() if isinstance(name, bytes) else str(name) + guid_str = guid_string_for_device_index(idx) + table.add_row(str(idx), "GameController" if is_gc else "Joystick", name_str, guid_str) + console.print(table) + + def prepare_pairing_state( args: argparse.Namespace, console: Console, @@ -804,7 +840,7 @@ def open_initial_contexts( pairing: PairingState, controller_indices: List[int], console: Console, - id_registry: ControllerIdRegistry, + config: BridgeConfig, ) -> Tuple[Dict[int, ControllerContext], List[PicoUART]]: """Open initial controllers and UARTs for detected indices.""" contexts: Dict[int, ControllerContext] = {} @@ -823,7 +859,9 @@ def open_initial_contexts( except Exception as exc: console.print(f"[red]Failed to open controller {index}: {exc}[/red]") continue - stable_id = id_registry.stable_id_for_guid(guid) + stable_id = guid + if index in config.swap_abxy_indices: + config.swap_abxy_ids.add(stable_id) uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) @@ -891,7 +929,7 @@ def handle_button_event( return current_button_map = ( config.button_map_swapped - if (args.swap_abxy or ctx.controller_index in config.swap_abxy_indices) + if (args.swap_abxy or ctx.stable_id in config.swap_abxy_ids) else config.button_map_default ) button = event.cbutton.button @@ -915,7 +953,7 @@ def handle_device_added( contexts: Dict[int, ControllerContext], uarts: List[PicoUART], console: Console, - id_registry: ControllerIdRegistry, + config: BridgeConfig, ) -> None: """Handle controller hotplug by opening and pairing UART if possible.""" idx = event.cdevice.which @@ -935,7 +973,10 @@ def handle_device_added( except Exception as exc: console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") return - stable_id = id_registry.stable_id_for_guid(guid) + stable_id = guid + # Promote any index-based swap flags to stable IDs on first sight. + if idx in config.swap_abxy_indices: + config.swap_abxy_ids.add(stable_id) uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) @@ -991,7 +1032,7 @@ def service_contexts( for ctx in list(contexts.values()): current_button_map = ( config.button_map_swapped - if (args.swap_abxy or ctx.controller_index in config.swap_abxy_indices) + if (args.swap_abxy or ctx.stable_id in config.swap_abxy_ids) else config.button_map_default ) poll_controller_buttons(ctx, current_button_map) @@ -1055,7 +1096,6 @@ def run_bridge_loop( pairing: PairingState, contexts: Dict[int, ControllerContext], uarts: List[PicoUART], - id_registry: ControllerIdRegistry, ) -> None: """Main event loop for bridging controllers to UART and handling rumble.""" event = sdl2.SDL_Event() @@ -1073,7 +1113,7 @@ def run_bridge_loop( elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): handle_button_event(event, args, config, contexts) elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: - handle_device_added(event, args, pairing, contexts, uarts, console, id_registry) + handle_device_added(event, args, pairing, contexts, uarts, console, config) elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: handle_device_removed(event, pairing, contexts, console) @@ -1105,16 +1145,18 @@ def main() -> None: console = Console() config = build_bridge_config(console, args) initialize_sdl(parser) - id_registry = ControllerIdRegistry() contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] try: + if args.list_controllers: + list_controllers_with_guids(console, parser) + return controller_indices, controller_names = detect_controllers(console, args, parser) pairing = prepare_pairing_state(args, console, parser, controller_indices, controller_names) - contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console, id_registry) + contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console, config) if not contexts: console.print("[yellow]No controllers opened; waiting for hotplug events...[/yellow]") - run_bridge_loop(args, console, config, pairing, contexts, uarts, id_registry) + run_bridge_loop(args, console, config, pairing, contexts, uarts) finally: cleanup(contexts, uarts) From 32c53605447d95ff9397bc603b56d5a129a37bd6 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 23 Nov 2025 10:08:59 -0700 Subject: [PATCH 052/134] Add GUID based selection --- README.md | 5 +- controller_uart_bridge.py | 100 +++++++++++++++++++++++++++----------- 2 files changed, 75 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index c4fd775..a61aa02 100644 --- a/README.md +++ b/README.md @@ -52,11 +52,14 @@ Options: - `--all-ports` to include non-USB serial devices in discovery. - `--ignore-port-desc SUBSTR` / `--include-port-desc SUBSTR` to filter serial ports by description (repeatable). - `--include-controller-name SUBSTR` to only open controllers whose name matches (repeatable). +- `--list-controllers` to print detected controllers and their GUIDs, then exit (useful for GUID-based options). - `--baud 921600` (default 921600; use `500000` if your adapter can’t do 900K). - `--frequency 1000` to send at 1 kHz. - `--deadzone 0.08` to change stick deadzone (0.0-1.0). - `--trigger-threshold 0.35` to change analog trigger press threshold (0.0-1.0). -- `--swap-abxy` or `--swap-abxy-index N` to flip AB/XY globally or for specific controller indices (repeatable). +- `--swap-abxy` to flip AB/XY globally. +- `--swap-abxy-index N` (repeatable) to flip AB/XY for controllers first seen at index N (auto-converts to a stable GUID). +- `--swap-abxy-guid GUID` (repeatable) to flip AB/XY for a specific physical controller (GUID is stable across runs). - `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). Hot‑plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 1a7f9b7..453e9cb 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -370,7 +370,7 @@ class ControllerContext: controller: sdl2.SDL_GameController instance_id: int controller_index: int - stable_id: int + stable_id: str port: Optional[str] uart: Optional[PicoUART] report: SwitchReport = field(default_factory=SwitchReport) @@ -392,10 +392,7 @@ def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int, str]: raise RuntimeError(f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}") joystick = sdl2.SDL_GameControllerGetJoystick(controller) instance_id = sdl2.SDL_JoystickInstanceID(joystick) - guid = sdl2.SDL_JoystickGetGUID(joystick) - buf = create_string_buffer(33) - sdl2.SDL_JoystickGetGUIDString(guid, buf, 33) - guid_str = buf.value.decode() if buf.value else "" + guid_str = guid_string_from_joystick(joystick) return controller, instance_id, guid_str @@ -407,6 +404,22 @@ def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: return None +def guid_string_from_joystick(joystick: sdl2.SDL_Joystick) -> str: + """Return a GUID string for an already-open joystick.""" + guid = sdl2.SDL_JoystickGetGUID(joystick) + buf = create_string_buffer(33) + sdl2.SDL_JoystickGetGUIDString(guid, buf, 33) + return buf.value.decode().lower() if buf.value else "" + + +def guid_string_for_device_index(index: int) -> str: + """Return a GUID string for a joystick device index without opening it.""" + guid = sdl2.SDL_JoystickGetDeviceGUID(index) + buf = create_string_buffer(33) + sdl2.SDL_JoystickGetGUIDString(guid, buf, 33) + return buf.value.decode().lower() if buf.value else "" + + def open_uart_or_warn(port: str, baud: int, console: Console) -> Optional[PicoUART]: """Open a UART and warn on failure.""" try: @@ -470,6 +483,11 @@ def build_arg_parser() -> argparse.ArgumentParser: default=[], help="Only open controllers whose name contains this substring (case-insensitive). Repeatable.", ) + parser.add_argument( + "--list-controllers", + action="store_true", + help="List detected controllers with GUIDs and exit.", + ) parser.add_argument( "--swap-abxy", action="store_true", @@ -482,6 +500,12 @@ def build_arg_parser() -> argparse.ArgumentParser: default=[], help="Swap AB/XY mapping for specific controller indices (repeatable).", ) + parser.add_argument( + "--swap-abxy-guid", + action="append", + default=[], + help="Swap AB/XY mapping for specific controller GUIDs (see --list-controllers). Repeatable.", + ) parser.add_argument( "--sdl-mapping", action="append", @@ -526,6 +550,7 @@ class BridgeConfig: button_map_default: Dict[int, int] button_map_swapped: Dict[int, int] swap_abxy_indices: set[int] + swap_abxy_ids: set[str] @dataclass @@ -540,19 +565,6 @@ class PairingState: include_port_desc: List[str] = field(default_factory=list) -@dataclass -class ControllerIdRegistry: - """Assign stable IDs to controllers based on their GUID.""" - guid_to_id: Dict[str, int] = field(default_factory=dict) - next_id: int = 0 - - def stable_id_for_guid(self, guid: str) -> int: - if guid not in self.guid_to_id: - self.guid_to_id[guid] = self.next_id - self.next_id += 1 - return self.guid_to_id[guid] - - def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, int], Dict[int, int], set[int]]: """Load SDL controller mappings and return button map variants.""" default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" @@ -582,6 +594,7 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) button_map_default, button_map_swapped, swap_abxy_indices = load_button_maps(console, args) + swap_abxy_guids = {g.lower() for g in args.swap_abxy_guid} return BridgeConfig( interval=interval, deadzone_raw=deadzone_raw, @@ -589,6 +602,7 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon button_map_default=button_map_default, button_map_swapped=button_map_swapped, swap_abxy_indices=swap_abxy_indices, + swap_abxy_ids=set(swap_abxy_guids), # filled later once stable IDs are known ) @@ -633,6 +647,28 @@ def detect_controllers( return controller_indices, controller_names +def list_controllers_with_guids(console: Console, parser: argparse.ArgumentParser) -> None: + """Print detected controllers with their GUID strings and exit.""" + count = sdl2.SDL_NumJoysticks() + if count < 0: + parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") + if count == 0: + console.print("[yellow]No controllers detected.[/yellow]") + return + table = Table(title="Detected Controllers (GUIDs)") + table.add_column("Index", justify="center") + table.add_column("Type") + table.add_column("Name") + table.add_column("GUID") + for idx in range(count): + is_gc = sdl2.SDL_IsGameController(idx) + name = sdl2.SDL_GameControllerNameForIndex(idx) if is_gc else sdl2.SDL_JoystickNameForIndex(idx) + name_str = name.decode() if isinstance(name, bytes) else str(name) + guid_str = guid_string_for_device_index(idx) + table.add_row(str(idx), "GameController" if is_gc else "Joystick", name_str, guid_str) + console.print(table) + + def prepare_pairing_state( args: argparse.Namespace, console: Console, @@ -804,7 +840,7 @@ def open_initial_contexts( pairing: PairingState, controller_indices: List[int], console: Console, - id_registry: ControllerIdRegistry, + config: BridgeConfig, ) -> Tuple[Dict[int, ControllerContext], List[PicoUART]]: """Open initial controllers and UARTs for detected indices.""" contexts: Dict[int, ControllerContext] = {} @@ -823,7 +859,9 @@ def open_initial_contexts( except Exception as exc: console.print(f"[red]Failed to open controller {index}: {exc}[/red]") continue - stable_id = id_registry.stable_id_for_guid(guid) + stable_id = guid + if index in config.swap_abxy_indices: + config.swap_abxy_ids.add(stable_id) uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) @@ -891,7 +929,7 @@ def handle_button_event( return current_button_map = ( config.button_map_swapped - if (args.swap_abxy or ctx.controller_index in config.swap_abxy_indices) + if (args.swap_abxy or ctx.stable_id in config.swap_abxy_ids) else config.button_map_default ) button = event.cbutton.button @@ -915,7 +953,7 @@ def handle_device_added( contexts: Dict[int, ControllerContext], uarts: List[PicoUART], console: Console, - id_registry: ControllerIdRegistry, + config: BridgeConfig, ) -> None: """Handle controller hotplug by opening and pairing UART if possible.""" idx = event.cdevice.which @@ -935,7 +973,10 @@ def handle_device_added( except Exception as exc: console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") return - stable_id = id_registry.stable_id_for_guid(guid) + stable_id = guid + # Promote any index-based swap flags to stable IDs on first sight. + if idx in config.swap_abxy_indices: + config.swap_abxy_ids.add(stable_id) uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) @@ -991,7 +1032,7 @@ def service_contexts( for ctx in list(contexts.values()): current_button_map = ( config.button_map_swapped - if (args.swap_abxy or ctx.controller_index in config.swap_abxy_indices) + if (args.swap_abxy or ctx.stable_id in config.swap_abxy_ids) else config.button_map_default ) poll_controller_buttons(ctx, current_button_map) @@ -1055,7 +1096,6 @@ def run_bridge_loop( pairing: PairingState, contexts: Dict[int, ControllerContext], uarts: List[PicoUART], - id_registry: ControllerIdRegistry, ) -> None: """Main event loop for bridging controllers to UART and handling rumble.""" event = sdl2.SDL_Event() @@ -1073,7 +1113,7 @@ def run_bridge_loop( elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): handle_button_event(event, args, config, contexts) elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: - handle_device_added(event, args, pairing, contexts, uarts, console, id_registry) + handle_device_added(event, args, pairing, contexts, uarts, console, config) elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: handle_device_removed(event, pairing, contexts, console) @@ -1105,16 +1145,18 @@ def main() -> None: console = Console() config = build_bridge_config(console, args) initialize_sdl(parser) - id_registry = ControllerIdRegistry() contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] try: + if args.list_controllers: + list_controllers_with_guids(console, parser) + return controller_indices, controller_names = detect_controllers(console, args, parser) pairing = prepare_pairing_state(args, console, parser, controller_indices, controller_names) - contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console, id_registry) + contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console, config) if not contexts: console.print("[yellow]No controllers opened; waiting for hotplug events...[/yellow]") - run_bridge_loop(args, console, config, pairing, contexts, uarts, id_registry) + run_bridge_loop(args, console, config, pairing, contexts, uarts) finally: cleanup(contexts, uarts) From 491a35888ad75c902d412b50b1a7d99651052d81 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 24 Nov 2025 22:40:51 -0700 Subject: [PATCH 053/134] Build with colors :) --- README.md | 4 +- build.sh | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100755 build.sh diff --git a/README.md b/README.md index a61aa02..cda43d8 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,9 @@ Flags: - `SWITCH_PICO_LOG`: enable/disable UART logging on the Pico. ### Changing controller colours -Edit `controller_color_config.h` (RGB values) and rebuild; the Switch will show the new colours on reconnect. +`./build.sh` now writes a random colour into `controller_color_config.h` before building so the Switch shows a fresh colour when you flash. +- Use `./build.sh --color FF00AA` (or `--color 12,34,56`) to pick a specific colour applied to the body/buttons/grips. +- Use `./build.sh --keep-color` if you want to build without touching `controller_color_config.h`. ## Python bridge (recommended) Works on macOS, Windows, Linux. Uses SDL2 + pyserial. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..4371d2e --- /dev/null +++ b/build.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd -- "$(dirname "$0")" && pwd)" +cd "$SCRIPT_DIR" + +CONFIG_FILE="$SCRIPT_DIR/controller_color_config.h" + +usage() { + cat <<'EOF' +Usage: build.sh [--random-grip-color | --grip-color RRGGBB] + --random-grip-color Randomize both grip colors before building. + --grip-color RRGGBB Set both grip colors to the provided hex value. + --help Show this help message. + +Default behavior leaves controller_color_config.h unchanged. +EOF +} + +ensure_single_color_mode() { + local current_mode="$1" + local new_mode="$2" + if [[ "$current_mode" != "none" && "$current_mode" != "$new_mode" ]]; then + echo "Error: Choose either --random-grip-color or --grip-color, not both." >&2 + exit 1 + fi +} + +update_grip_colors() { + local rgb_hex="$1" + local r="${rgb_hex:0:2}" + local g="${rgb_hex:2:2}" + local b="${rgb_hex:4:2}" + + if [[ ! -f "$CONFIG_FILE" ]]; then + echo "Error: Cannot find $CONFIG_FILE" >&2 + exit 1 + fi + + python - "$CONFIG_FILE" "$r" "$g" "$b" <<'PY' +import re +import sys +path, r, g, b = sys.argv[1:5] + +try: + with open(path, "r", encoding="utf-8") as f: + text = f.read() +except OSError as exc: + sys.stderr.write(f"Error reading {path}: {exc}\n") + sys.exit(1) + +def replace(name, val, data): + pattern = rf"(?m)^(#define\s+{name}\s+)0x[0-9A-Fa-f]{{2}}" + updated, count = re.subn(pattern, rf"\g<1>0x{val.upper()}", data) + if count == 0: + sys.stderr.write(f"Error: Could not find {name} in {path}\n") + sys.exit(1) + return updated + +for macro, val in [ + ("SWITCH_COLOR_LEFT_GRIP_R", r), + ("SWITCH_COLOR_LEFT_GRIP_G", g), + ("SWITCH_COLOR_LEFT_GRIP_B", b), + ("SWITCH_COLOR_RIGHT_GRIP_R", r), + ("SWITCH_COLOR_RIGHT_GRIP_G", g), + ("SWITCH_COLOR_RIGHT_GRIP_B", b), +]: + text = replace(macro, val, text) + +try: + with open(path, "w", encoding="utf-8") as f: + f.write(text) +except OSError as exc: + sys.stderr.write(f"Error writing {path}: {exc}\n") + sys.exit(1) +PY +} + +GRIP_MODE="none" +CUSTOM_COLOR="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --random-grip-color) + ensure_single_color_mode "$GRIP_MODE" "random" + GRIP_MODE="random" + shift + ;; + --grip-color) + ensure_single_color_mode "$GRIP_MODE" "custom" + GRIP_MODE="custom" + CUSTOM_COLOR="${2-}" + if [[ -z "$CUSTOM_COLOR" ]]; then + echo "Error: --grip-color requires a hex value like FF8800." >&2 + exit 1 + fi + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage + exit 1 + ;; + esac +done + +if [[ "$GRIP_MODE" == "random" ]]; then + CUSTOM_COLOR="$(printf "%02X%02X%02X" $((RANDOM % 256)) $((RANDOM % 256)) $((RANDOM % 256)))" +elif [[ "$GRIP_MODE" == "custom" ]]; then + if ! [[ "$CUSTOM_COLOR" =~ ^[0-9A-Fa-f]{6}$ ]]; then + echo "Error: Color must be a 6-digit hex value like FF8800." >&2 + exit 1 + fi +fi + +if [[ "$GRIP_MODE" != "none" ]]; then + update_grip_colors "$CUSTOM_COLOR" + echo "Grip color set to #$CUSTOM_COLOR in controller_color_config.h" +fi + +cmake -S . -B build -DSWITCH_PICO_AUTOTEST=OFF -DSWITCH_PICO_LOG=OFF && cmake --build build +/Users/joey/.pico-sdk/picotool/2.2.0-a4/picotool/picotool load /Users/joey/Projects/switch-pico/build/switch-pico.elf -fx From c7ef19fe232deb01edc8e383e5591aacdbfcc233 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 24 Nov 2025 22:40:51 -0700 Subject: [PATCH 054/134] Build with colors :) --- README.md | 4 +- build.sh | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100755 build.sh diff --git a/README.md b/README.md index a61aa02..cda43d8 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,9 @@ Flags: - `SWITCH_PICO_LOG`: enable/disable UART logging on the Pico. ### Changing controller colours -Edit `controller_color_config.h` (RGB values) and rebuild; the Switch will show the new colours on reconnect. +`./build.sh` now writes a random colour into `controller_color_config.h` before building so the Switch shows a fresh colour when you flash. +- Use `./build.sh --color FF00AA` (or `--color 12,34,56`) to pick a specific colour applied to the body/buttons/grips. +- Use `./build.sh --keep-color` if you want to build without touching `controller_color_config.h`. ## Python bridge (recommended) Works on macOS, Windows, Linux. Uses SDL2 + pyserial. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..4371d2e --- /dev/null +++ b/build.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd -- "$(dirname "$0")" && pwd)" +cd "$SCRIPT_DIR" + +CONFIG_FILE="$SCRIPT_DIR/controller_color_config.h" + +usage() { + cat <<'EOF' +Usage: build.sh [--random-grip-color | --grip-color RRGGBB] + --random-grip-color Randomize both grip colors before building. + --grip-color RRGGBB Set both grip colors to the provided hex value. + --help Show this help message. + +Default behavior leaves controller_color_config.h unchanged. +EOF +} + +ensure_single_color_mode() { + local current_mode="$1" + local new_mode="$2" + if [[ "$current_mode" != "none" && "$current_mode" != "$new_mode" ]]; then + echo "Error: Choose either --random-grip-color or --grip-color, not both." >&2 + exit 1 + fi +} + +update_grip_colors() { + local rgb_hex="$1" + local r="${rgb_hex:0:2}" + local g="${rgb_hex:2:2}" + local b="${rgb_hex:4:2}" + + if [[ ! -f "$CONFIG_FILE" ]]; then + echo "Error: Cannot find $CONFIG_FILE" >&2 + exit 1 + fi + + python - "$CONFIG_FILE" "$r" "$g" "$b" <<'PY' +import re +import sys +path, r, g, b = sys.argv[1:5] + +try: + with open(path, "r", encoding="utf-8") as f: + text = f.read() +except OSError as exc: + sys.stderr.write(f"Error reading {path}: {exc}\n") + sys.exit(1) + +def replace(name, val, data): + pattern = rf"(?m)^(#define\s+{name}\s+)0x[0-9A-Fa-f]{{2}}" + updated, count = re.subn(pattern, rf"\g<1>0x{val.upper()}", data) + if count == 0: + sys.stderr.write(f"Error: Could not find {name} in {path}\n") + sys.exit(1) + return updated + +for macro, val in [ + ("SWITCH_COLOR_LEFT_GRIP_R", r), + ("SWITCH_COLOR_LEFT_GRIP_G", g), + ("SWITCH_COLOR_LEFT_GRIP_B", b), + ("SWITCH_COLOR_RIGHT_GRIP_R", r), + ("SWITCH_COLOR_RIGHT_GRIP_G", g), + ("SWITCH_COLOR_RIGHT_GRIP_B", b), +]: + text = replace(macro, val, text) + +try: + with open(path, "w", encoding="utf-8") as f: + f.write(text) +except OSError as exc: + sys.stderr.write(f"Error writing {path}: {exc}\n") + sys.exit(1) +PY +} + +GRIP_MODE="none" +CUSTOM_COLOR="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --random-grip-color) + ensure_single_color_mode "$GRIP_MODE" "random" + GRIP_MODE="random" + shift + ;; + --grip-color) + ensure_single_color_mode "$GRIP_MODE" "custom" + GRIP_MODE="custom" + CUSTOM_COLOR="${2-}" + if [[ -z "$CUSTOM_COLOR" ]]; then + echo "Error: --grip-color requires a hex value like FF8800." >&2 + exit 1 + fi + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage + exit 1 + ;; + esac +done + +if [[ "$GRIP_MODE" == "random" ]]; then + CUSTOM_COLOR="$(printf "%02X%02X%02X" $((RANDOM % 256)) $((RANDOM % 256)) $((RANDOM % 256)))" +elif [[ "$GRIP_MODE" == "custom" ]]; then + if ! [[ "$CUSTOM_COLOR" =~ ^[0-9A-Fa-f]{6}$ ]]; then + echo "Error: Color must be a 6-digit hex value like FF8800." >&2 + exit 1 + fi +fi + +if [[ "$GRIP_MODE" != "none" ]]; then + update_grip_colors "$CUSTOM_COLOR" + echo "Grip color set to #$CUSTOM_COLOR in controller_color_config.h" +fi + +cmake -S . -B build -DSWITCH_PICO_AUTOTEST=OFF -DSWITCH_PICO_LOG=OFF && cmake --build build +/Users/joey/.pico-sdk/picotool/2.2.0-a4/picotool/picotool load /Users/joey/Projects/switch-pico/build/switch-pico.elf -fx From 396d8ba33def58a0f951df7f7d9feefe0be29b8b Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Tue, 25 Nov 2025 06:23:01 -0700 Subject: [PATCH 055/134] Refactor into lib --- README.md | 15 ++ controller_uart_bridge.py | 200 ++--------------------- switch_pico_uart.py | 328 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 359 insertions(+), 184 deletions(-) create mode 100644 switch_pico_uart.py diff --git a/README.md b/README.md index cda43d8..c513afd 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,21 @@ Options: Hot‑plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. +### Using the lightweight UART helper (no SDL needed) +For simple scripts or tests you can skip SDL and drive the Pico directly with `switch_pico_uart.py`: +```python +from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchHat + +with SwitchUARTClient("/dev/cu.usbserial-0001") as client: + client.press(SwitchButton.A) + client.release(SwitchButton.A) + client.move_left_stick(0.0, -1.0) # push up + client.set_hat(SwitchHat.TOP_RIGHT) + print(client.poll_rumble()) # returns (left, right) amplitudes 0.0-1.0 or None +``` +- `SwitchButton` is an `IntFlag` (bitwise friendly) and `SwitchHat` is an `IntEnum` for the DPAD/hat values. +- The helper only depends on `pyserial`; SDL is not required. + ### macOS tips - Ensure the USB‑serial adapter shows up (use `/dev/cu.usb*` for TX). - Some controllers’ Guide/Home buttons are intercepted by macOS; using XInput/DInput mode or disabling Steam’s controller handling helps. diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 453e9cb..cd05e17 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -16,15 +16,12 @@ Features inspired by ``host/controller_bridge.py``: from __future__ import annotations import argparse -import struct -import threading import time from dataclasses import dataclass, field from ctypes import create_string_buffer from pathlib import Path from typing import Dict, List, Optional, Tuple -import serial from serial import SerialException from serial.tools import list_ports from serial.tools import list_ports_common @@ -35,46 +32,24 @@ from rich.console import Console from rich.prompt import Prompt from rich.table import Table -UART_HEADER = 0xAA -RUMBLE_HEADER = 0xBB -RUMBLE_TYPE_RUMBLE = 0x01 -UART_BAUD = 921600 +from switch_pico_uart import ( + UART_BAUD, + PicoUART, + SwitchButton, + SwitchHat, + SwitchReport, + axis_to_stick, + dpad_to_hat, + decode_rumble, + trigger_to_button, +) + RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this RUMBLE_MIN_ACTIVE = 0.50 # below this, rumble is treated as off/noise RUMBLE_SCALE = 0.8 -class SwitchButton: - # Mirrors the masks defined in switch_pro_descriptors.h - Y = 1 << 0 - B = 1 << 1 - A = 1 << 2 - X = 1 << 3 - L = 1 << 4 - R = 1 << 5 - ZL = 1 << 6 - ZR = 1 << 7 - MINUS = 1 << 8 - PLUS = 1 << 9 - LCLICK = 1 << 10 - RCLICK = 1 << 11 - HOME = 1 << 12 - CAPTURE = 1 << 13 - - -class SwitchHat: - TOP = 0x00 - TOP_RIGHT = 0x01 - RIGHT = 0x02 - BOTTOM_RIGHT = 0x03 - BOTTOM = 0x04 - BOTTOM_LEFT = 0x05 - LEFT = 0x06 - TOP_LEFT = 0x07 - CENTER = 0x08 - - def parse_mapping(value: str) -> Tuple[int, str]: """Parse 'index:serial_port' CLI mapping argument.""" if ":" not in value: @@ -89,19 +64,6 @@ def parse_mapping(value: str) -> Tuple[int, str]: return idx, port.strip() -def axis_to_stick(value: int, deadzone: int) -> int: - """Convert a signed SDL axis value to 0-255 stick range with deadzone.""" - if abs(value) < deadzone: - value = 0 - scaled = int((value + 32768) * 255 / 65535) - return max(0, min(255, scaled)) - - -def trigger_to_button(value: int, threshold: int) -> bool: - """Return True if analog trigger crosses digital threshold.""" - return value >= threshold - - def set_hint(name: str, value: str) -> None: """Set an SDL hint safely even if the constant is missing in PySDL2.""" try: @@ -134,32 +96,6 @@ DPAD_BUTTONS = { } -def dpad_to_hat(flags: Dict[str, bool]) -> int: - """Translate DPAD button flags into a Switch hat value.""" - up = flags["up"] - down = flags["down"] - left = flags["left"] - right = flags["right"] - - if up and right: - return SwitchHat.TOP_RIGHT - if up and left: - return SwitchHat.TOP_LEFT - if down and right: - return SwitchHat.BOTTOM_RIGHT - if down and left: - return SwitchHat.BOTTOM_LEFT - if up: - return SwitchHat.TOP - if down: - return SwitchHat.BOTTOM - if right: - return SwitchHat.RIGHT - if left: - return SwitchHat.LEFT - return SwitchHat.CENTER - - def is_usb_serial(path: str) -> bool: """Heuristic for USB serial path prefixes.""" if path.startswith("/dev/tty.") and not path.startswith("/dev/tty.usb"): @@ -244,110 +180,6 @@ def interactive_pairing( return mappings -@dataclass -class SwitchReport: - buttons: int = 0 - hat: int = SwitchHat.CENTER - lx: int = 128 - ly: int = 128 - rx: int = 128 - ry: int = 128 - - def to_bytes(self) -> bytes: - """Serialize the report into the UART packet format.""" - return struct.pack( - " None: - """Open a UART connection to the Pico with non-blocking IO.""" - self.serial = serial.Serial( - port=port, - baudrate=baudrate, - bytesize=serial.EIGHTBITS, - stopbits=serial.STOPBITS_ONE, - parity=serial.PARITY_NONE, - timeout=0.0, - write_timeout=0.0, - xonxoff=False, - rtscts=False, - dsrdtr=False, - ) - self._buffer = bytearray() - - def send_report(self, report: SwitchReport) -> None: - """Send a controller report to the Pico.""" - # Non-blocking write; no flush to avoid sync stalls. - self.serial.write(report.to_bytes()) - - def read_rumble_payload(self) -> Optional[bytes]: - """ - Drain all currently available UART bytes into an internal buffer, - then try to extract a single valid rumble frame. - - Frame format: - 0: 0xBB (RUMBLE_HEADER) - 1: type (0x01 for rumble) - 2-9: 8-byte rumble payload - 10: checksum (sum of first 10 bytes) & 0xFF - """ - # Read whatever is waiting in OS buffer - waiting = self.serial.in_waiting - if waiting: - self._buffer.extend(self.serial.read(waiting)) - - while True: - if not self._buffer: - return None - - start = self._buffer.find(bytes([RUMBLE_HEADER])) - if start < 0: - # No header at all, drop garbage - self._buffer.clear() - return None - - # Not enough data for a full frame yet - if len(self._buffer) - start < 11: - if start > 0: - del self._buffer[:start] - return None - - frame = self._buffer[start:start + 11] - checksum = sum(frame[:10]) & 0xFF - - if frame[1] == RUMBLE_TYPE_RUMBLE and checksum == frame[10]: - payload = bytes(frame[2:10]) - del self._buffer[:start + 11] - return payload - - # Bad frame, drop this header and resync to the next candidate - del self._buffer[:start + 1] - - def close(self) -> None: - """Close the UART connection.""" - self.serial.close() - - -def decode_rumble(payload: bytes) -> Tuple[float, float]: - """Return normalized rumble amplitudes (0.0-1.0) for left/right.""" - if len(payload) < 8: - return 0.0, 0.0 - # Neutral/idle pattern used by Switch: no rumble energy. - if payload == b"\x00\x01\x40\x40\x00\x01\x40\x40": - return 0.0, 0.0 - # Rumble amp is 10 bits across bytes 0/1 and 4/5. - # Switch format is right rumble first, then left rumble (4 bytes each). - right_raw = ((payload[1] & 0x03) << 8) | payload[0] - left_raw = ((payload[5] & 0x03) << 8) | payload[4] - if left_raw < 8 and right_raw < 8: - return 0.0, 0.0 - left = min(max(left_raw / 1023.0, 0.0), 1.0) - right = min(max(right_raw / 1023.0, 0.0), 1.0) - return left, right - - def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> float: """Apply rumble payload to SDL controller and return max normalized energy.""" left_norm, right_norm = decode_rumble(payload) @@ -515,7 +347,7 @@ def build_arg_parser() -> argparse.ArgumentParser: return parser -def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, int]) -> None: +def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, SwitchButton]) -> None: """Update button/hat state based on current SDL controller readings.""" changed = False for sdl_button, switch_bit in button_map.items(): @@ -547,8 +379,8 @@ class BridgeConfig: interval: float deadzone_raw: int trigger_threshold: int - button_map_default: Dict[int, int] - button_map_swapped: Dict[int, int] + button_map_default: Dict[int, SwitchButton] + button_map_swapped: Dict[int, SwitchButton] swap_abxy_indices: set[int] swap_abxy_ids: set[str] @@ -565,7 +397,7 @@ class PairingState: include_port_desc: List[str] = field(default_factory=list) -def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, int], Dict[int, int], set[int]]: +def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, SwitchButton], Dict[int, SwitchButton], set[int]]: """Load SDL controller mappings and return button map variants.""" default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" mappings_to_load: List[str] = [] diff --git a/switch_pico_uart.py b/switch_pico_uart.py new file mode 100644 index 0000000..be49d42 --- /dev/null +++ b/switch_pico_uart.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python3 +""" +Lightweight helpers for talking to the switch-pico firmware over UART. + +This module exposes the raw report structure plus a small convenience wrapper +so other scripts can do things like "press a button" or "move a stick" without +depending on SDL. It mirrors the framing in ``switch-pico.cpp``: + + Host -> Pico : 0xAA, buttons (LE16), hat, lx, ly, rx, ry + Pico -> Host : 0xBB, 0x01, 8 rumble bytes, checksum (sum of first 10 bytes) +""" + +from __future__ import annotations + +import struct +import time +from dataclasses import dataclass, field +from enum import IntEnum, IntFlag +from typing import Iterable, Mapping, Optional, Tuple, Union + +import serial + +UART_HEADER = 0xAA +RUMBLE_HEADER = 0xBB +RUMBLE_TYPE_RUMBLE = 0x01 +UART_BAUD = 921600 + + +class SwitchButton(IntFlag): + # Mirrors the masks defined in switch_pro_descriptors.h + Y = 1 << 0 + B = 1 << 1 + A = 1 << 2 + X = 1 << 3 + L = 1 << 4 + R = 1 << 5 + ZL = 1 << 6 + ZR = 1 << 7 + MINUS = 1 << 8 + PLUS = 1 << 9 + LCLICK = 1 << 10 + RCLICK = 1 << 11 + HOME = 1 << 12 + CAPTURE = 1 << 13 + + +class SwitchHat(IntEnum): + TOP = 0x00 + TOP_RIGHT = 0x01 + RIGHT = 0x02 + BOTTOM_RIGHT = 0x03 + BOTTOM = 0x04 + BOTTOM_LEFT = 0x05 + LEFT = 0x06 + TOP_LEFT = 0x07 + CENTER = 0x08 + + +def clamp_byte(value: Union[int, float]) -> int: + """Clamp a numeric value to the 0-255 byte range.""" + return max(0, min(255, int(value))) + + +def normalize_stick_value(value: Union[int, float]) -> int: + """ + Convert a normalized float (-1..1) or raw byte (0..255) to the stick range. + + Floats are treated as -1.0 = full negative deflection, 0.0 = center, + 1.0 = full positive deflection. Integers are assumed to already be in the + 0-255 range. + """ + if isinstance(value, float): + value = max(-1.0, min(1.0, value)) + value = int(round((value + 1.0) * 255 / 2.0)) + return clamp_byte(value) + + +def axis_to_stick(value: int, deadzone: int) -> int: + """Convert a signed axis value to 0-255 stick range with deadzone.""" + if abs(value) < deadzone: + value = 0 + scaled = int((value + 32768) * 255 / 65535) + return clamp_byte(scaled) + + +def trigger_to_button(value: int, threshold: int) -> bool: + """Return True if analog trigger crosses digital threshold.""" + return value >= threshold + + +def dpad_to_hat(flags: Mapping[str, bool]) -> SwitchHat: + """Translate DPAD button flags into a Switch hat value.""" + up = flags.get("up", False) + down = flags.get("down", False) + left = flags.get("left", False) + right = flags.get("right", False) + + if up and right: + return SwitchHat.TOP_RIGHT + if up and left: + return SwitchHat.TOP_LEFT + if down and right: + return SwitchHat.BOTTOM_RIGHT + if down and left: + return SwitchHat.BOTTOM_LEFT + if up: + return SwitchHat.TOP + if down: + return SwitchHat.BOTTOM + if right: + return SwitchHat.RIGHT + if left: + return SwitchHat.LEFT + return SwitchHat.CENTER + + +@dataclass +class SwitchReport: + buttons: int = 0 + hat: SwitchHat = SwitchHat.CENTER + lx: int = 128 + ly: int = 128 + rx: int = 128 + ry: int = 128 + + def to_bytes(self) -> bytes: + """Serialize the report into the UART packet format.""" + return struct.pack( + " None: + """Open a UART connection to the Pico with non-blocking IO.""" + self.serial = serial.Serial( + port=port, + baudrate=baudrate, + bytesize=serial.EIGHTBITS, + stopbits=serial.STOPBITS_ONE, + parity=serial.PARITY_NONE, + timeout=0.0, + write_timeout=0.0, + xonxoff=False, + rtscts=False, + dsrdtr=False, + ) + self._buffer = bytearray() + + def send_report(self, report: SwitchReport) -> None: + """Send a controller report to the Pico.""" + self.serial.write(report.to_bytes()) + + def read_rumble_payload(self) -> Optional[bytes]: + """ + Drain available UART bytes into an internal buffer, then extract one rumble frame. + + Frame format: + 0: 0xBB (RUMBLE_HEADER) + 1: type (0x01 for rumble) + 2-9: 8-byte rumble payload + 10: checksum (sum of first 10 bytes) & 0xFF + """ + waiting = self.serial.in_waiting + if waiting: + self._buffer.extend(self.serial.read(waiting)) + + while True: + if not self._buffer: + return None + + start = self._buffer.find(bytes([RUMBLE_HEADER])) + if start < 0: + self._buffer.clear() + return None + + if len(self._buffer) - start < 11: + if start > 0: + del self._buffer[:start] + return None + + frame = self._buffer[start:start + 11] + checksum = sum(frame[:10]) & 0xFF + + if frame[1] == RUMBLE_TYPE_RUMBLE and checksum == frame[10]: + payload = bytes(frame[2:10]) + del self._buffer[:start + 11] + return payload + + del self._buffer[:start + 1] + + def close(self) -> None: + """Close the UART connection.""" + self.serial.close() + + +def decode_rumble(payload: bytes) -> Tuple[float, float]: + """Return normalized rumble amplitudes (0.0-1.0) for left/right.""" + if len(payload) < 8: + return 0.0, 0.0 + if payload == b"\x00\x01\x40\x40\x00\x01\x40\x40": + return 0.0, 0.0 + right_raw = ((payload[1] & 0x03) << 8) | payload[0] + left_raw = ((payload[5] & 0x03) << 8) | payload[4] + if left_raw < 8 and right_raw < 8: + return 0.0, 0.0 + left = min(max(left_raw / 1023.0, 0.0), 1.0) + right = min(max(right_raw / 1023.0, 0.0), 1.0) + return left, right + + +@dataclass +class SwitchControllerState: + """Mutable controller state with helpers for building reports.""" + + report: SwitchReport = field(default_factory=SwitchReport) + + def press(self, *buttons: Union[SwitchButton, int]) -> None: + """Set one or more buttons as pressed.""" + for button in buttons: + self.report.buttons |= int(button) + + def release(self, *buttons: Union[SwitchButton, int]) -> None: + """Release one or more buttons.""" + for button in buttons: + self.report.buttons &= ~int(button) + + def set_buttons(self, buttons: Iterable[Union[SwitchButton, int]]) -> None: + """Replace the current button bitmask with the provided buttons.""" + self.report.buttons = 0 + self.press(*buttons) + + def set_hat(self, hat: Union[SwitchHat, int]) -> None: + """Set the DPAD/hat value directly.""" + self.report.hat = int(hat) & 0xFF + + def move_left_stick(self, x: Union[int, float], y: Union[int, float]) -> None: + """Move the left stick using normalized floats (-1..1) or raw bytes (0-255).""" + self.report.lx = normalize_stick_value(x) + self.report.ly = normalize_stick_value(y) + + def move_right_stick(self, x: Union[int, float], y: Union[int, float]) -> None: + """Move the right stick using normalized floats (-1..1) or raw bytes (0-255).""" + self.report.rx = normalize_stick_value(x) + self.report.ry = normalize_stick_value(y) + + def neutral(self) -> None: + """Clear all input back to the neutral controller state.""" + self.report.buttons = 0 + self.report.hat = SwitchHat.CENTER + self.report.lx = 128 + self.report.ly = 128 + self.report.rx = 128 + self.report.ry = 128 + + +class SwitchUARTClient: + """ + High-level helper to send controller actions to the Pico and poll rumble. + + Example: + with SwitchUARTClient("/dev/cu.usbserial-0001") as client: + client.press(SwitchButton.A) + time.sleep(0.1) + client.release(SwitchButton.A) + client.move_left_stick(0.0, -1.0) # push up + """ + + def __init__(self, port: str, baud: int = UART_BAUD, send_interval: float = 0.0) -> None: + self.uart = PicoUART(port, baud) + self.state = SwitchControllerState() + self.send_interval = max(0.0, send_interval) + self._last_send = 0.0 + + def send(self) -> None: + """Send the current state to the Pico, throttled by send_interval if set.""" + now = time.monotonic() + if self.send_interval and (now - self._last_send) < self.send_interval: + return + self.uart.send_report(self.state.report) + self._last_send = now + + def press(self, *buttons: int) -> None: + self.state.press(*buttons) + self.send() + + def release(self, *buttons: int) -> None: + self.state.release(*buttons) + self.send() + + def set_buttons(self, buttons: Iterable[int]) -> None: + self.state.set_buttons(buttons) + self.send() + + def set_hat(self, hat: int) -> None: + self.state.set_hat(hat) + self.send() + + def move_left_stick(self, x: Union[int, float], y: Union[int, float]) -> None: + self.state.move_left_stick(x, y) + self.send() + + def move_right_stick(self, x: Union[int, float], y: Union[int, float]) -> None: + self.state.move_right_stick(x, y) + self.send() + + def neutral(self) -> None: + self.state.neutral() + self.send() + + def poll_rumble(self) -> Optional[Tuple[float, float]]: + """ + Poll for the latest rumble payload and return normalized amplitudes. + Returns None if no rumble frame was available. + """ + payload = self.uart.read_rumble_payload() + if payload: + return decode_rumble(payload) + return None + + def close(self) -> None: + self.uart.close() + + def __enter__(self) -> "SwitchUARTClient": + return self + + def __exit__(self, exc_type, exc, tb) -> None: + self.close() From 1bd428c45c8286c204851bc10bc4016fc2ec22d8 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Tue, 25 Nov 2025 06:23:01 -0700 Subject: [PATCH 056/134] Refactor into lib --- README.md | 15 ++ controller_uart_bridge.py | 200 ++--------------------- switch_pico_uart.py | 328 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 359 insertions(+), 184 deletions(-) create mode 100644 switch_pico_uart.py diff --git a/README.md b/README.md index cda43d8..c513afd 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,21 @@ Options: Hot‑plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. +### Using the lightweight UART helper (no SDL needed) +For simple scripts or tests you can skip SDL and drive the Pico directly with `switch_pico_uart.py`: +```python +from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchHat + +with SwitchUARTClient("/dev/cu.usbserial-0001") as client: + client.press(SwitchButton.A) + client.release(SwitchButton.A) + client.move_left_stick(0.0, -1.0) # push up + client.set_hat(SwitchHat.TOP_RIGHT) + print(client.poll_rumble()) # returns (left, right) amplitudes 0.0-1.0 or None +``` +- `SwitchButton` is an `IntFlag` (bitwise friendly) and `SwitchHat` is an `IntEnum` for the DPAD/hat values. +- The helper only depends on `pyserial`; SDL is not required. + ### macOS tips - Ensure the USB‑serial adapter shows up (use `/dev/cu.usb*` for TX). - Some controllers’ Guide/Home buttons are intercepted by macOS; using XInput/DInput mode or disabling Steam’s controller handling helps. diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 453e9cb..cd05e17 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -16,15 +16,12 @@ Features inspired by ``host/controller_bridge.py``: from __future__ import annotations import argparse -import struct -import threading import time from dataclasses import dataclass, field from ctypes import create_string_buffer from pathlib import Path from typing import Dict, List, Optional, Tuple -import serial from serial import SerialException from serial.tools import list_ports from serial.tools import list_ports_common @@ -35,46 +32,24 @@ from rich.console import Console from rich.prompt import Prompt from rich.table import Table -UART_HEADER = 0xAA -RUMBLE_HEADER = 0xBB -RUMBLE_TYPE_RUMBLE = 0x01 -UART_BAUD = 921600 +from switch_pico_uart import ( + UART_BAUD, + PicoUART, + SwitchButton, + SwitchHat, + SwitchReport, + axis_to_stick, + dpad_to_hat, + decode_rumble, + trigger_to_button, +) + RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this RUMBLE_MIN_ACTIVE = 0.50 # below this, rumble is treated as off/noise RUMBLE_SCALE = 0.8 -class SwitchButton: - # Mirrors the masks defined in switch_pro_descriptors.h - Y = 1 << 0 - B = 1 << 1 - A = 1 << 2 - X = 1 << 3 - L = 1 << 4 - R = 1 << 5 - ZL = 1 << 6 - ZR = 1 << 7 - MINUS = 1 << 8 - PLUS = 1 << 9 - LCLICK = 1 << 10 - RCLICK = 1 << 11 - HOME = 1 << 12 - CAPTURE = 1 << 13 - - -class SwitchHat: - TOP = 0x00 - TOP_RIGHT = 0x01 - RIGHT = 0x02 - BOTTOM_RIGHT = 0x03 - BOTTOM = 0x04 - BOTTOM_LEFT = 0x05 - LEFT = 0x06 - TOP_LEFT = 0x07 - CENTER = 0x08 - - def parse_mapping(value: str) -> Tuple[int, str]: """Parse 'index:serial_port' CLI mapping argument.""" if ":" not in value: @@ -89,19 +64,6 @@ def parse_mapping(value: str) -> Tuple[int, str]: return idx, port.strip() -def axis_to_stick(value: int, deadzone: int) -> int: - """Convert a signed SDL axis value to 0-255 stick range with deadzone.""" - if abs(value) < deadzone: - value = 0 - scaled = int((value + 32768) * 255 / 65535) - return max(0, min(255, scaled)) - - -def trigger_to_button(value: int, threshold: int) -> bool: - """Return True if analog trigger crosses digital threshold.""" - return value >= threshold - - def set_hint(name: str, value: str) -> None: """Set an SDL hint safely even if the constant is missing in PySDL2.""" try: @@ -134,32 +96,6 @@ DPAD_BUTTONS = { } -def dpad_to_hat(flags: Dict[str, bool]) -> int: - """Translate DPAD button flags into a Switch hat value.""" - up = flags["up"] - down = flags["down"] - left = flags["left"] - right = flags["right"] - - if up and right: - return SwitchHat.TOP_RIGHT - if up and left: - return SwitchHat.TOP_LEFT - if down and right: - return SwitchHat.BOTTOM_RIGHT - if down and left: - return SwitchHat.BOTTOM_LEFT - if up: - return SwitchHat.TOP - if down: - return SwitchHat.BOTTOM - if right: - return SwitchHat.RIGHT - if left: - return SwitchHat.LEFT - return SwitchHat.CENTER - - def is_usb_serial(path: str) -> bool: """Heuristic for USB serial path prefixes.""" if path.startswith("/dev/tty.") and not path.startswith("/dev/tty.usb"): @@ -244,110 +180,6 @@ def interactive_pairing( return mappings -@dataclass -class SwitchReport: - buttons: int = 0 - hat: int = SwitchHat.CENTER - lx: int = 128 - ly: int = 128 - rx: int = 128 - ry: int = 128 - - def to_bytes(self) -> bytes: - """Serialize the report into the UART packet format.""" - return struct.pack( - " None: - """Open a UART connection to the Pico with non-blocking IO.""" - self.serial = serial.Serial( - port=port, - baudrate=baudrate, - bytesize=serial.EIGHTBITS, - stopbits=serial.STOPBITS_ONE, - parity=serial.PARITY_NONE, - timeout=0.0, - write_timeout=0.0, - xonxoff=False, - rtscts=False, - dsrdtr=False, - ) - self._buffer = bytearray() - - def send_report(self, report: SwitchReport) -> None: - """Send a controller report to the Pico.""" - # Non-blocking write; no flush to avoid sync stalls. - self.serial.write(report.to_bytes()) - - def read_rumble_payload(self) -> Optional[bytes]: - """ - Drain all currently available UART bytes into an internal buffer, - then try to extract a single valid rumble frame. - - Frame format: - 0: 0xBB (RUMBLE_HEADER) - 1: type (0x01 for rumble) - 2-9: 8-byte rumble payload - 10: checksum (sum of first 10 bytes) & 0xFF - """ - # Read whatever is waiting in OS buffer - waiting = self.serial.in_waiting - if waiting: - self._buffer.extend(self.serial.read(waiting)) - - while True: - if not self._buffer: - return None - - start = self._buffer.find(bytes([RUMBLE_HEADER])) - if start < 0: - # No header at all, drop garbage - self._buffer.clear() - return None - - # Not enough data for a full frame yet - if len(self._buffer) - start < 11: - if start > 0: - del self._buffer[:start] - return None - - frame = self._buffer[start:start + 11] - checksum = sum(frame[:10]) & 0xFF - - if frame[1] == RUMBLE_TYPE_RUMBLE and checksum == frame[10]: - payload = bytes(frame[2:10]) - del self._buffer[:start + 11] - return payload - - # Bad frame, drop this header and resync to the next candidate - del self._buffer[:start + 1] - - def close(self) -> None: - """Close the UART connection.""" - self.serial.close() - - -def decode_rumble(payload: bytes) -> Tuple[float, float]: - """Return normalized rumble amplitudes (0.0-1.0) for left/right.""" - if len(payload) < 8: - return 0.0, 0.0 - # Neutral/idle pattern used by Switch: no rumble energy. - if payload == b"\x00\x01\x40\x40\x00\x01\x40\x40": - return 0.0, 0.0 - # Rumble amp is 10 bits across bytes 0/1 and 4/5. - # Switch format is right rumble first, then left rumble (4 bytes each). - right_raw = ((payload[1] & 0x03) << 8) | payload[0] - left_raw = ((payload[5] & 0x03) << 8) | payload[4] - if left_raw < 8 and right_raw < 8: - return 0.0, 0.0 - left = min(max(left_raw / 1023.0, 0.0), 1.0) - right = min(max(right_raw / 1023.0, 0.0), 1.0) - return left, right - - def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> float: """Apply rumble payload to SDL controller and return max normalized energy.""" left_norm, right_norm = decode_rumble(payload) @@ -515,7 +347,7 @@ def build_arg_parser() -> argparse.ArgumentParser: return parser -def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, int]) -> None: +def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, SwitchButton]) -> None: """Update button/hat state based on current SDL controller readings.""" changed = False for sdl_button, switch_bit in button_map.items(): @@ -547,8 +379,8 @@ class BridgeConfig: interval: float deadzone_raw: int trigger_threshold: int - button_map_default: Dict[int, int] - button_map_swapped: Dict[int, int] + button_map_default: Dict[int, SwitchButton] + button_map_swapped: Dict[int, SwitchButton] swap_abxy_indices: set[int] swap_abxy_ids: set[str] @@ -565,7 +397,7 @@ class PairingState: include_port_desc: List[str] = field(default_factory=list) -def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, int], Dict[int, int], set[int]]: +def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, SwitchButton], Dict[int, SwitchButton], set[int]]: """Load SDL controller mappings and return button map variants.""" default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" mappings_to_load: List[str] = [] diff --git a/switch_pico_uart.py b/switch_pico_uart.py new file mode 100644 index 0000000..be49d42 --- /dev/null +++ b/switch_pico_uart.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python3 +""" +Lightweight helpers for talking to the switch-pico firmware over UART. + +This module exposes the raw report structure plus a small convenience wrapper +so other scripts can do things like "press a button" or "move a stick" without +depending on SDL. It mirrors the framing in ``switch-pico.cpp``: + + Host -> Pico : 0xAA, buttons (LE16), hat, lx, ly, rx, ry + Pico -> Host : 0xBB, 0x01, 8 rumble bytes, checksum (sum of first 10 bytes) +""" + +from __future__ import annotations + +import struct +import time +from dataclasses import dataclass, field +from enum import IntEnum, IntFlag +from typing import Iterable, Mapping, Optional, Tuple, Union + +import serial + +UART_HEADER = 0xAA +RUMBLE_HEADER = 0xBB +RUMBLE_TYPE_RUMBLE = 0x01 +UART_BAUD = 921600 + + +class SwitchButton(IntFlag): + # Mirrors the masks defined in switch_pro_descriptors.h + Y = 1 << 0 + B = 1 << 1 + A = 1 << 2 + X = 1 << 3 + L = 1 << 4 + R = 1 << 5 + ZL = 1 << 6 + ZR = 1 << 7 + MINUS = 1 << 8 + PLUS = 1 << 9 + LCLICK = 1 << 10 + RCLICK = 1 << 11 + HOME = 1 << 12 + CAPTURE = 1 << 13 + + +class SwitchHat(IntEnum): + TOP = 0x00 + TOP_RIGHT = 0x01 + RIGHT = 0x02 + BOTTOM_RIGHT = 0x03 + BOTTOM = 0x04 + BOTTOM_LEFT = 0x05 + LEFT = 0x06 + TOP_LEFT = 0x07 + CENTER = 0x08 + + +def clamp_byte(value: Union[int, float]) -> int: + """Clamp a numeric value to the 0-255 byte range.""" + return max(0, min(255, int(value))) + + +def normalize_stick_value(value: Union[int, float]) -> int: + """ + Convert a normalized float (-1..1) or raw byte (0..255) to the stick range. + + Floats are treated as -1.0 = full negative deflection, 0.0 = center, + 1.0 = full positive deflection. Integers are assumed to already be in the + 0-255 range. + """ + if isinstance(value, float): + value = max(-1.0, min(1.0, value)) + value = int(round((value + 1.0) * 255 / 2.0)) + return clamp_byte(value) + + +def axis_to_stick(value: int, deadzone: int) -> int: + """Convert a signed axis value to 0-255 stick range with deadzone.""" + if abs(value) < deadzone: + value = 0 + scaled = int((value + 32768) * 255 / 65535) + return clamp_byte(scaled) + + +def trigger_to_button(value: int, threshold: int) -> bool: + """Return True if analog trigger crosses digital threshold.""" + return value >= threshold + + +def dpad_to_hat(flags: Mapping[str, bool]) -> SwitchHat: + """Translate DPAD button flags into a Switch hat value.""" + up = flags.get("up", False) + down = flags.get("down", False) + left = flags.get("left", False) + right = flags.get("right", False) + + if up and right: + return SwitchHat.TOP_RIGHT + if up and left: + return SwitchHat.TOP_LEFT + if down and right: + return SwitchHat.BOTTOM_RIGHT + if down and left: + return SwitchHat.BOTTOM_LEFT + if up: + return SwitchHat.TOP + if down: + return SwitchHat.BOTTOM + if right: + return SwitchHat.RIGHT + if left: + return SwitchHat.LEFT + return SwitchHat.CENTER + + +@dataclass +class SwitchReport: + buttons: int = 0 + hat: SwitchHat = SwitchHat.CENTER + lx: int = 128 + ly: int = 128 + rx: int = 128 + ry: int = 128 + + def to_bytes(self) -> bytes: + """Serialize the report into the UART packet format.""" + return struct.pack( + " None: + """Open a UART connection to the Pico with non-blocking IO.""" + self.serial = serial.Serial( + port=port, + baudrate=baudrate, + bytesize=serial.EIGHTBITS, + stopbits=serial.STOPBITS_ONE, + parity=serial.PARITY_NONE, + timeout=0.0, + write_timeout=0.0, + xonxoff=False, + rtscts=False, + dsrdtr=False, + ) + self._buffer = bytearray() + + def send_report(self, report: SwitchReport) -> None: + """Send a controller report to the Pico.""" + self.serial.write(report.to_bytes()) + + def read_rumble_payload(self) -> Optional[bytes]: + """ + Drain available UART bytes into an internal buffer, then extract one rumble frame. + + Frame format: + 0: 0xBB (RUMBLE_HEADER) + 1: type (0x01 for rumble) + 2-9: 8-byte rumble payload + 10: checksum (sum of first 10 bytes) & 0xFF + """ + waiting = self.serial.in_waiting + if waiting: + self._buffer.extend(self.serial.read(waiting)) + + while True: + if not self._buffer: + return None + + start = self._buffer.find(bytes([RUMBLE_HEADER])) + if start < 0: + self._buffer.clear() + return None + + if len(self._buffer) - start < 11: + if start > 0: + del self._buffer[:start] + return None + + frame = self._buffer[start:start + 11] + checksum = sum(frame[:10]) & 0xFF + + if frame[1] == RUMBLE_TYPE_RUMBLE and checksum == frame[10]: + payload = bytes(frame[2:10]) + del self._buffer[:start + 11] + return payload + + del self._buffer[:start + 1] + + def close(self) -> None: + """Close the UART connection.""" + self.serial.close() + + +def decode_rumble(payload: bytes) -> Tuple[float, float]: + """Return normalized rumble amplitudes (0.0-1.0) for left/right.""" + if len(payload) < 8: + return 0.0, 0.0 + if payload == b"\x00\x01\x40\x40\x00\x01\x40\x40": + return 0.0, 0.0 + right_raw = ((payload[1] & 0x03) << 8) | payload[0] + left_raw = ((payload[5] & 0x03) << 8) | payload[4] + if left_raw < 8 and right_raw < 8: + return 0.0, 0.0 + left = min(max(left_raw / 1023.0, 0.0), 1.0) + right = min(max(right_raw / 1023.0, 0.0), 1.0) + return left, right + + +@dataclass +class SwitchControllerState: + """Mutable controller state with helpers for building reports.""" + + report: SwitchReport = field(default_factory=SwitchReport) + + def press(self, *buttons: Union[SwitchButton, int]) -> None: + """Set one or more buttons as pressed.""" + for button in buttons: + self.report.buttons |= int(button) + + def release(self, *buttons: Union[SwitchButton, int]) -> None: + """Release one or more buttons.""" + for button in buttons: + self.report.buttons &= ~int(button) + + def set_buttons(self, buttons: Iterable[Union[SwitchButton, int]]) -> None: + """Replace the current button bitmask with the provided buttons.""" + self.report.buttons = 0 + self.press(*buttons) + + def set_hat(self, hat: Union[SwitchHat, int]) -> None: + """Set the DPAD/hat value directly.""" + self.report.hat = int(hat) & 0xFF + + def move_left_stick(self, x: Union[int, float], y: Union[int, float]) -> None: + """Move the left stick using normalized floats (-1..1) or raw bytes (0-255).""" + self.report.lx = normalize_stick_value(x) + self.report.ly = normalize_stick_value(y) + + def move_right_stick(self, x: Union[int, float], y: Union[int, float]) -> None: + """Move the right stick using normalized floats (-1..1) or raw bytes (0-255).""" + self.report.rx = normalize_stick_value(x) + self.report.ry = normalize_stick_value(y) + + def neutral(self) -> None: + """Clear all input back to the neutral controller state.""" + self.report.buttons = 0 + self.report.hat = SwitchHat.CENTER + self.report.lx = 128 + self.report.ly = 128 + self.report.rx = 128 + self.report.ry = 128 + + +class SwitchUARTClient: + """ + High-level helper to send controller actions to the Pico and poll rumble. + + Example: + with SwitchUARTClient("/dev/cu.usbserial-0001") as client: + client.press(SwitchButton.A) + time.sleep(0.1) + client.release(SwitchButton.A) + client.move_left_stick(0.0, -1.0) # push up + """ + + def __init__(self, port: str, baud: int = UART_BAUD, send_interval: float = 0.0) -> None: + self.uart = PicoUART(port, baud) + self.state = SwitchControllerState() + self.send_interval = max(0.0, send_interval) + self._last_send = 0.0 + + def send(self) -> None: + """Send the current state to the Pico, throttled by send_interval if set.""" + now = time.monotonic() + if self.send_interval and (now - self._last_send) < self.send_interval: + return + self.uart.send_report(self.state.report) + self._last_send = now + + def press(self, *buttons: int) -> None: + self.state.press(*buttons) + self.send() + + def release(self, *buttons: int) -> None: + self.state.release(*buttons) + self.send() + + def set_buttons(self, buttons: Iterable[int]) -> None: + self.state.set_buttons(buttons) + self.send() + + def set_hat(self, hat: int) -> None: + self.state.set_hat(hat) + self.send() + + def move_left_stick(self, x: Union[int, float], y: Union[int, float]) -> None: + self.state.move_left_stick(x, y) + self.send() + + def move_right_stick(self, x: Union[int, float], y: Union[int, float]) -> None: + self.state.move_right_stick(x, y) + self.send() + + def neutral(self) -> None: + self.state.neutral() + self.send() + + def poll_rumble(self) -> Optional[Tuple[float, float]]: + """ + Poll for the latest rumble payload and return normalized amplitudes. + Returns None if no rumble frame was available. + """ + payload = self.uart.read_rumble_payload() + if payload: + return decode_rumble(payload) + return None + + def close(self) -> None: + self.uart.close() + + def __enter__(self) -> "SwitchUARTClient": + return self + + def __exit__(self, exc_type, exc, tb) -> None: + self.close() From ac5fc7be4487e1205bcedcf834ceee6d7fb74316 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Tue, 25 Nov 2025 11:37:09 -0700 Subject: [PATCH 057/134] add requirements.txt --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6bb2756 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +pyserial +pysdl2 +rich From 70564d87cab67b6e1e6773f38f3d9dafeb9a5809 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Tue, 25 Nov 2025 11:37:09 -0700 Subject: [PATCH 058/134] add requirements.txt --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6bb2756 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +pyserial +pysdl2 +rich From 6191333abb6e5d87f6dfb01f35d774a6a3f96d7d Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Tue, 25 Nov 2025 16:24:42 -0700 Subject: [PATCH 059/134] Remove rust --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index c513afd..7ed9320 100644 --- a/README.md +++ b/README.md @@ -92,9 +92,6 @@ with SwitchUARTClient("/dev/cu.usbserial-0001") as client: ### Linux tips - You may need udev permissions for `/dev/ttyUSB*`/`/dev/ttyACM*` (add user to `dialout`/`uucp` or use `udev` rules). -## Rust bridge (optional) -Note: Rust toolchain not currently present in this setup. If you install Rust + SDL2 dev libraries, an experimental Rust bridge lives in `fast_uart_bridge/`. - ## Troubleshooting - **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, Switch sees a “Pro Controller”. - **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `controller_uart_bridge.py` if needed. From 05ea1a3f9177bea665531827a97227930da1d147 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Tue, 25 Nov 2025 16:24:42 -0700 Subject: [PATCH 060/134] Remove rust --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index c513afd..7ed9320 100644 --- a/README.md +++ b/README.md @@ -92,9 +92,6 @@ with SwitchUARTClient("/dev/cu.usbserial-0001") as client: ### Linux tips - You may need udev permissions for `/dev/ttyUSB*`/`/dev/ttyACM*` (add user to `dialout`/`uucp` or use `udev` rules). -## Rust bridge (optional) -Note: Rust toolchain not currently present in this setup. If you install Rust + SDL2 dev libraries, an experimental Rust bridge lives in `fast_uart_bridge/`. - ## Troubleshooting - **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, Switch sees a “Pro Controller”. - **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `controller_uart_bridge.py` if needed. From 5ce26f21816238bc52f2568d2e21d4b66e88cffe Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Tue, 25 Nov 2025 17:51:21 -0700 Subject: [PATCH 061/134] Tighten serial detection for linux --- controller_uart_bridge.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index cd05e17..b630217 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -97,12 +97,27 @@ DPAD_BUTTONS = { def is_usb_serial(path: str) -> bool: - """Heuristic for USB serial path prefixes.""" - if path.startswith("/dev/tty.") and not path.startswith("/dev/tty.usb"): + """ + Heuristic for USB serial path prefixes (best-effort when VID/PID are missing). + + Accepts common USB-adapter patterns; rejects generic /dev/tty* unless they + clearly indicate USB. + """ + lower = path.lower() + usb_prefixes = ( + "/dev/ttyusb", # Linux USB serial + "/dev/ttyacm", # Linux CDC ACM + "/dev/cu.usb", # macOS cu/tty USB adapters + "/dev/tty.usb", + ) + if lower.startswith(usb_prefixes): + return True + if "usb" in lower: + return True + if lower.startswith(("/dev/tty", "/dev/cu", "com")): return False - if path.startswith("/dev/cu.") and not path.startswith("/dev/cu.usb"): - return False - return True + # Default to False for unknown paths; caller can include_non_usb to override. + return False def is_usb_serial_port(port: list_ports_common.ListPortInfo) -> bool: From fba378e8e69916a35b0ea53625cbed97ee2bba9c Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Tue, 25 Nov 2025 17:51:21 -0700 Subject: [PATCH 062/134] Tighten serial detection for linux --- controller_uart_bridge.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index cd05e17..b630217 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -97,12 +97,27 @@ DPAD_BUTTONS = { def is_usb_serial(path: str) -> bool: - """Heuristic for USB serial path prefixes.""" - if path.startswith("/dev/tty.") and not path.startswith("/dev/tty.usb"): + """ + Heuristic for USB serial path prefixes (best-effort when VID/PID are missing). + + Accepts common USB-adapter patterns; rejects generic /dev/tty* unless they + clearly indicate USB. + """ + lower = path.lower() + usb_prefixes = ( + "/dev/ttyusb", # Linux USB serial + "/dev/ttyacm", # Linux CDC ACM + "/dev/cu.usb", # macOS cu/tty USB adapters + "/dev/tty.usb", + ) + if lower.startswith(usb_prefixes): + return True + if "usb" in lower: + return True + if lower.startswith(("/dev/tty", "/dev/cu", "com")): return False - if path.startswith("/dev/cu.") and not path.startswith("/dev/cu.usb"): - return False - return True + # Default to False for unknown paths; caller can include_non_usb to override. + return False def is_usb_serial_port(port: list_ports_common.ListPortInfo) -> bool: From 80528ff39e236ae2c69b88512fa0441520e69a1b Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Tue, 25 Nov 2025 17:57:14 -0700 Subject: [PATCH 063/134] Make more sane search --- controller_uart_bridge.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index b630217..e48f166 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -112,10 +112,6 @@ def is_usb_serial(path: str) -> bool: ) if lower.startswith(usb_prefixes): return True - if "usb" in lower: - return True - if lower.startswith(("/dev/tty", "/dev/cu", "com")): - return False # Default to False for unknown paths; caller can include_non_usb to override. return False From fd1095d1cfce35607241ef94e3efe8233dcc5fc4 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Tue, 25 Nov 2025 17:57:14 -0700 Subject: [PATCH 064/134] Make more sane search --- controller_uart_bridge.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index b630217..e48f166 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -112,10 +112,6 @@ def is_usb_serial(path: str) -> bool: ) if lower.startswith(usb_prefixes): return True - if "usb" in lower: - return True - if lower.startswith(("/dev/tty", "/dev/cu", "com")): - return False # Default to False for unknown paths; caller can include_non_usb to override. return False From 8e740d4fffcd86449dad0c2f741477b5c400787d Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Sat, 29 Nov 2025 20:54:15 -0700 Subject: [PATCH 065/134] Add zeroing ability --- README.md | 2 + controller_uart_bridge.py | 186 +++++++++++++++++++++++++++++++++++++- 2 files changed, 185 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7ed9320..e2a9e07 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,8 @@ Options: - `--baud 921600` (default 921600; use `500000` if your adapter can’t do 900K). - `--frequency 1000` to send at 1 kHz. - `--deadzone 0.08` to change stick deadzone (0.0-1.0). +- `--zero-sticks` to sample the current stick positions on connect and treat them as neutral (cancel drift). +- `--zero-hotkey z` to choose the terminal hotkey that re-zeroes all connected controllers on demand (press `z` by default; pass an empty string to disable). - `--trigger-threshold 0.35` to change analog trigger press threshold (0.0-1.0). - `--swap-abxy` to flip AB/XY globally. - `--swap-abxy-index N` (repeatable) to flip AB/XY for controllers first seen at index N (auto-converts to a stable GUID). diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index e48f166..33701c2 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -16,6 +16,8 @@ Features inspired by ``host/controller_bridge.py``: from __future__ import annotations import argparse +import os +import sys import time from dataclasses import dataclass, field from ctypes import create_string_buffer @@ -64,6 +66,18 @@ def parse_mapping(value: str) -> Tuple[int, str]: return idx, port.strip() +def parse_hotkey(value: str) -> str: + """Validate a single-character hotkey (empty string disables).""" + if value is None: + return "" + value = value.strip() + if not value: + return "" + if len(value) != 1: + raise argparse.ArgumentTypeError("Hotkeys must be a single character (or empty to disable).") + return value + + def set_hint(name: str, value: str) -> None: """Set an SDL hint safely even if the constant is missing in PySDL2.""" try: @@ -95,6 +109,15 @@ DPAD_BUTTONS = { sdl2.SDL_CONTROLLER_BUTTON_DPAD_RIGHT: "right", } +STICK_AXIS_LABELS = ( + (sdl2.SDL_CONTROLLER_AXIS_LEFTX, "LX"), + (sdl2.SDL_CONTROLLER_AXIS_LEFTY, "LY"), + (sdl2.SDL_CONTROLLER_AXIS_RIGHTX, "RX"), + (sdl2.SDL_CONTROLLER_AXIS_RIGHTY, "RY"), +) + +STICK_AXES = tuple(axis for axis, _ in STICK_AXIS_LABELS) + def is_usb_serial(path: str) -> bool: """ @@ -226,6 +249,128 @@ class ControllerContext: last_rumble_change: float = 0.0 last_rumble_energy: float = 0.0 rumble_active: bool = False + axis_offsets: Dict[int, int] = field(default_factory=dict) + + +def capture_stick_offsets(controller: sdl2.SDL_GameController) -> Dict[int, int]: + """Sample the current stick axes so they can be treated as the neutral center.""" + offsets: Dict[int, int] = {} + for axis in STICK_AXES: + offsets[axis] = int(sdl2.SDL_GameControllerGetAxis(controller, axis)) + return offsets + + +def format_axis_offsets(offsets: Dict[int, int]) -> str: + """Return a human-friendly summary of per-axis offsets (for logging).""" + return ", ".join(f"{label}={offsets.get(axis, 0):+d}" for axis, label in STICK_AXIS_LABELS) + + +def calibrate_axis_value(value: int, axis: int, ctx: ControllerContext) -> int: + """Apply any stored calibration offset to a raw axis reading.""" + if not ctx.axis_offsets: + return value + offset = ctx.axis_offsets.get(axis) + if offset is None: + return value + return max(-32768, min(32767, value - offset)) + + +class ZeroHotkeyMonitor: + """Platform-aware helper that watches for a single keypress without blocking the main loop.""" + + def __init__(self, key: str, console: Console) -> None: + self.key = (key or "").lower() + self.console = console + self._active = False + self._platform = os.name + self._fd: Optional[int] = None + self._old_termios = None + self._msvcrt = None + + def start(self) -> bool: + if not self.key: + return False + if self._platform == "nt": + try: + import msvcrt # type: ignore + except ImportError: + self.console.print("[yellow]Zero hotkey disabled: msvcrt unavailable.[/yellow]") + return False + self._msvcrt = msvcrt + self._active = True + self.console.print( + f"[magenta]Press '{self.key.upper()}' in this terminal to re-zero controller sticks.[/magenta]" + ) + return True + + if not sys.stdin.isatty(): + self.console.print("[yellow]Zero hotkey disabled: stdin is not a TTY.[/yellow]") + return False + import termios + import tty + + self._fd = sys.stdin.fileno() + self._old_termios = termios.tcgetattr(self._fd) + tty.setcbreak(self._fd) + self._active = True + self.console.print( + f"[magenta]Press '{self.key.upper()}' in this terminal to re-zero controller sticks.[/magenta]" + ) + return True + + def stop(self) -> None: + if not self._active: + return + if self._platform != "nt" and self._fd is not None and self._old_termios is not None: + import termios + + termios.tcsetattr(self._fd, termios.TCSADRAIN, self._old_termios) + self._active = False + + def poll_trigger(self) -> bool: + if not self._active: + return False + key = self._read_key() + if key and key.lower() == self.key: + return True + return False + + def _read_key(self) -> Optional[str]: + if self._platform == "nt": + if self._msvcrt and self._msvcrt.kbhit(): + ch = self._msvcrt.getwch() + if ch == "\x03": + raise KeyboardInterrupt + return ch + return None + import select + + ready, _, _ = select.select([sys.stdin], [], [], 0) + if not ready: + return None + ch = sys.stdin.read(1) + if ch == "\x03": + raise KeyboardInterrupt + return ch + + +def zero_context_sticks(ctx: ControllerContext, console: Optional[Console] = None, reason: str = "Zeroed stick centers") -> None: + """Capture and store the current stick positions for a controller.""" + offsets = capture_stick_offsets(ctx.controller) + ctx.axis_offsets = offsets + if console: + console.print( + f"[cyan]{reason} for controller {ctx.controller_index} (inst {ctx.instance_id}): {format_axis_offsets(offsets)}[/cyan]" + ) + + +def zero_all_context_sticks(contexts: Dict[int, ControllerContext], console: Console) -> None: + """Zero every connected controller's sticks.""" + if not contexts: + console.print("[yellow]No controllers available to zero right now.[/yellow]") + return + for ctx in contexts.values(): + zero_context_sticks(ctx, console, reason="Re-zeroed stick centers") def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int, str]: @@ -295,7 +440,24 @@ def build_arg_parser() -> argparse.ArgumentParser: default=500.0, help="Report send frequency per controller (Hz, default 500)", ) - parser.add_argument("--deadzone", type=float, default=0.08, help="Stick deadzone (0.0-1.0, default 0.08)") + parser.add_argument( + "--deadzone", + type=float, + default=0.08, + help="Stick deadzone (0.0-1.0, default 0.08)", + ) + parser.add_argument( + "--zero-sticks", + action="store_true", + help="Capture stick positions on connect and treat them as neutral to cancel drift.", + ) + parser.add_argument( + "--zero-hotkey", + type=parse_hotkey, + default="z", + metavar="KEY", + help="Press this key in the terminal to re-zero sticks at runtime (default: 'z', empty string disables).", + ) parser.add_argument( "--trigger-threshold", type=float, @@ -390,6 +552,8 @@ class BridgeConfig: interval: float deadzone_raw: int trigger_threshold: int + zero_sticks: bool + zero_hotkey: str button_map_default: Dict[int, SwitchButton] button_map_swapped: Dict[int, SwitchButton] swap_abxy_indices: set[int] @@ -442,6 +606,8 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon interval=interval, deadzone_raw=deadzone_raw, trigger_threshold=trigger_threshold, + zero_sticks=bool(args.zero_sticks), + zero_hotkey=args.zero_hotkey or "", button_map_default=button_map_default, button_map_swapped=button_map_swapped, swap_abxy_indices=swap_abxy_indices, @@ -723,6 +889,8 @@ def open_initial_contexts( port=port, uart=uart, ) + if config.zero_sticks: + zero_context_sticks(ctx, console) contexts[instance_id] = ctx return contexts, uarts @@ -733,7 +901,7 @@ def handle_axis_motion(event: sdl2.SDL_Event, contexts: Dict[int, ControllerCont if not ctx: return axis = event.caxis.axis - value = event.caxis.value + value = calibrate_axis_value(event.caxis.value, axis, ctx) if axis == sdl2.SDL_CONTROLLER_AXIS_LEFTX: ctx.report.lx = axis_to_stick(value, config.deadzone_raw) elif axis == sdl2.SDL_CONTROLLER_AXIS_LEFTY: @@ -838,6 +1006,8 @@ def handle_device_added( port=port, uart=uart, ) + if config.zero_sticks: + zero_context_sticks(ctx, console) contexts[instance_id] = ctx @@ -939,6 +1109,7 @@ def run_bridge_loop( pairing: PairingState, contexts: Dict[int, ControllerContext], uarts: List[PicoUART], + hotkey: Optional[ZeroHotkeyMonitor] = None, ) -> None: """Main event loop for bridging controllers to UART and handling rumble.""" event = sdl2.SDL_Event() @@ -969,6 +1140,8 @@ def run_bridge_loop( else: pair_waiting_contexts(args, pairing, contexts, uarts, console) service_contexts(now, args, config, contexts, uarts, console) + if hotkey and hotkey.poll_trigger(): + zero_all_context_sticks(contexts, console) sdl2.SDL_Delay(1) @@ -990,17 +1163,24 @@ def main() -> None: initialize_sdl(parser) contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] + hotkey_monitor: Optional[ZeroHotkeyMonitor] = None try: if args.list_controllers: list_controllers_with_guids(console, parser) return controller_indices, controller_names = detect_controllers(console, args, parser) pairing = prepare_pairing_state(args, console, parser, controller_indices, controller_names) + if config.zero_hotkey: + candidate = ZeroHotkeyMonitor(config.zero_hotkey, console) + if candidate.start(): + hotkey_monitor = candidate contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console, config) if not contexts: console.print("[yellow]No controllers opened; waiting for hotplug events...[/yellow]") - run_bridge_loop(args, console, config, pairing, contexts, uarts) + run_bridge_loop(args, console, config, pairing, contexts, uarts, hotkey_monitor) finally: + if hotkey_monitor: + hotkey_monitor.stop() cleanup(contexts, uarts) From 703338ce580bc717e943713fdc6ceb059c1be319 Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Sat, 29 Nov 2025 20:54:15 -0700 Subject: [PATCH 066/134] Add zeroing ability --- README.md | 2 + controller_uart_bridge.py | 186 +++++++++++++++++++++++++++++++++++++- 2 files changed, 185 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7ed9320..e2a9e07 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,8 @@ Options: - `--baud 921600` (default 921600; use `500000` if your adapter can’t do 900K). - `--frequency 1000` to send at 1 kHz. - `--deadzone 0.08` to change stick deadzone (0.0-1.0). +- `--zero-sticks` to sample the current stick positions on connect and treat them as neutral (cancel drift). +- `--zero-hotkey z` to choose the terminal hotkey that re-zeroes all connected controllers on demand (press `z` by default; pass an empty string to disable). - `--trigger-threshold 0.35` to change analog trigger press threshold (0.0-1.0). - `--swap-abxy` to flip AB/XY globally. - `--swap-abxy-index N` (repeatable) to flip AB/XY for controllers first seen at index N (auto-converts to a stable GUID). diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index e48f166..33701c2 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -16,6 +16,8 @@ Features inspired by ``host/controller_bridge.py``: from __future__ import annotations import argparse +import os +import sys import time from dataclasses import dataclass, field from ctypes import create_string_buffer @@ -64,6 +66,18 @@ def parse_mapping(value: str) -> Tuple[int, str]: return idx, port.strip() +def parse_hotkey(value: str) -> str: + """Validate a single-character hotkey (empty string disables).""" + if value is None: + return "" + value = value.strip() + if not value: + return "" + if len(value) != 1: + raise argparse.ArgumentTypeError("Hotkeys must be a single character (or empty to disable).") + return value + + def set_hint(name: str, value: str) -> None: """Set an SDL hint safely even if the constant is missing in PySDL2.""" try: @@ -95,6 +109,15 @@ DPAD_BUTTONS = { sdl2.SDL_CONTROLLER_BUTTON_DPAD_RIGHT: "right", } +STICK_AXIS_LABELS = ( + (sdl2.SDL_CONTROLLER_AXIS_LEFTX, "LX"), + (sdl2.SDL_CONTROLLER_AXIS_LEFTY, "LY"), + (sdl2.SDL_CONTROLLER_AXIS_RIGHTX, "RX"), + (sdl2.SDL_CONTROLLER_AXIS_RIGHTY, "RY"), +) + +STICK_AXES = tuple(axis for axis, _ in STICK_AXIS_LABELS) + def is_usb_serial(path: str) -> bool: """ @@ -226,6 +249,128 @@ class ControllerContext: last_rumble_change: float = 0.0 last_rumble_energy: float = 0.0 rumble_active: bool = False + axis_offsets: Dict[int, int] = field(default_factory=dict) + + +def capture_stick_offsets(controller: sdl2.SDL_GameController) -> Dict[int, int]: + """Sample the current stick axes so they can be treated as the neutral center.""" + offsets: Dict[int, int] = {} + for axis in STICK_AXES: + offsets[axis] = int(sdl2.SDL_GameControllerGetAxis(controller, axis)) + return offsets + + +def format_axis_offsets(offsets: Dict[int, int]) -> str: + """Return a human-friendly summary of per-axis offsets (for logging).""" + return ", ".join(f"{label}={offsets.get(axis, 0):+d}" for axis, label in STICK_AXIS_LABELS) + + +def calibrate_axis_value(value: int, axis: int, ctx: ControllerContext) -> int: + """Apply any stored calibration offset to a raw axis reading.""" + if not ctx.axis_offsets: + return value + offset = ctx.axis_offsets.get(axis) + if offset is None: + return value + return max(-32768, min(32767, value - offset)) + + +class ZeroHotkeyMonitor: + """Platform-aware helper that watches for a single keypress without blocking the main loop.""" + + def __init__(self, key: str, console: Console) -> None: + self.key = (key or "").lower() + self.console = console + self._active = False + self._platform = os.name + self._fd: Optional[int] = None + self._old_termios = None + self._msvcrt = None + + def start(self) -> bool: + if not self.key: + return False + if self._platform == "nt": + try: + import msvcrt # type: ignore + except ImportError: + self.console.print("[yellow]Zero hotkey disabled: msvcrt unavailable.[/yellow]") + return False + self._msvcrt = msvcrt + self._active = True + self.console.print( + f"[magenta]Press '{self.key.upper()}' in this terminal to re-zero controller sticks.[/magenta]" + ) + return True + + if not sys.stdin.isatty(): + self.console.print("[yellow]Zero hotkey disabled: stdin is not a TTY.[/yellow]") + return False + import termios + import tty + + self._fd = sys.stdin.fileno() + self._old_termios = termios.tcgetattr(self._fd) + tty.setcbreak(self._fd) + self._active = True + self.console.print( + f"[magenta]Press '{self.key.upper()}' in this terminal to re-zero controller sticks.[/magenta]" + ) + return True + + def stop(self) -> None: + if not self._active: + return + if self._platform != "nt" and self._fd is not None and self._old_termios is not None: + import termios + + termios.tcsetattr(self._fd, termios.TCSADRAIN, self._old_termios) + self._active = False + + def poll_trigger(self) -> bool: + if not self._active: + return False + key = self._read_key() + if key and key.lower() == self.key: + return True + return False + + def _read_key(self) -> Optional[str]: + if self._platform == "nt": + if self._msvcrt and self._msvcrt.kbhit(): + ch = self._msvcrt.getwch() + if ch == "\x03": + raise KeyboardInterrupt + return ch + return None + import select + + ready, _, _ = select.select([sys.stdin], [], [], 0) + if not ready: + return None + ch = sys.stdin.read(1) + if ch == "\x03": + raise KeyboardInterrupt + return ch + + +def zero_context_sticks(ctx: ControllerContext, console: Optional[Console] = None, reason: str = "Zeroed stick centers") -> None: + """Capture and store the current stick positions for a controller.""" + offsets = capture_stick_offsets(ctx.controller) + ctx.axis_offsets = offsets + if console: + console.print( + f"[cyan]{reason} for controller {ctx.controller_index} (inst {ctx.instance_id}): {format_axis_offsets(offsets)}[/cyan]" + ) + + +def zero_all_context_sticks(contexts: Dict[int, ControllerContext], console: Console) -> None: + """Zero every connected controller's sticks.""" + if not contexts: + console.print("[yellow]No controllers available to zero right now.[/yellow]") + return + for ctx in contexts.values(): + zero_context_sticks(ctx, console, reason="Re-zeroed stick centers") def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int, str]: @@ -295,7 +440,24 @@ def build_arg_parser() -> argparse.ArgumentParser: default=500.0, help="Report send frequency per controller (Hz, default 500)", ) - parser.add_argument("--deadzone", type=float, default=0.08, help="Stick deadzone (0.0-1.0, default 0.08)") + parser.add_argument( + "--deadzone", + type=float, + default=0.08, + help="Stick deadzone (0.0-1.0, default 0.08)", + ) + parser.add_argument( + "--zero-sticks", + action="store_true", + help="Capture stick positions on connect and treat them as neutral to cancel drift.", + ) + parser.add_argument( + "--zero-hotkey", + type=parse_hotkey, + default="z", + metavar="KEY", + help="Press this key in the terminal to re-zero sticks at runtime (default: 'z', empty string disables).", + ) parser.add_argument( "--trigger-threshold", type=float, @@ -390,6 +552,8 @@ class BridgeConfig: interval: float deadzone_raw: int trigger_threshold: int + zero_sticks: bool + zero_hotkey: str button_map_default: Dict[int, SwitchButton] button_map_swapped: Dict[int, SwitchButton] swap_abxy_indices: set[int] @@ -442,6 +606,8 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon interval=interval, deadzone_raw=deadzone_raw, trigger_threshold=trigger_threshold, + zero_sticks=bool(args.zero_sticks), + zero_hotkey=args.zero_hotkey or "", button_map_default=button_map_default, button_map_swapped=button_map_swapped, swap_abxy_indices=swap_abxy_indices, @@ -723,6 +889,8 @@ def open_initial_contexts( port=port, uart=uart, ) + if config.zero_sticks: + zero_context_sticks(ctx, console) contexts[instance_id] = ctx return contexts, uarts @@ -733,7 +901,7 @@ def handle_axis_motion(event: sdl2.SDL_Event, contexts: Dict[int, ControllerCont if not ctx: return axis = event.caxis.axis - value = event.caxis.value + value = calibrate_axis_value(event.caxis.value, axis, ctx) if axis == sdl2.SDL_CONTROLLER_AXIS_LEFTX: ctx.report.lx = axis_to_stick(value, config.deadzone_raw) elif axis == sdl2.SDL_CONTROLLER_AXIS_LEFTY: @@ -838,6 +1006,8 @@ def handle_device_added( port=port, uart=uart, ) + if config.zero_sticks: + zero_context_sticks(ctx, console) contexts[instance_id] = ctx @@ -939,6 +1109,7 @@ def run_bridge_loop( pairing: PairingState, contexts: Dict[int, ControllerContext], uarts: List[PicoUART], + hotkey: Optional[ZeroHotkeyMonitor] = None, ) -> None: """Main event loop for bridging controllers to UART and handling rumble.""" event = sdl2.SDL_Event() @@ -969,6 +1140,8 @@ def run_bridge_loop( else: pair_waiting_contexts(args, pairing, contexts, uarts, console) service_contexts(now, args, config, contexts, uarts, console) + if hotkey and hotkey.poll_trigger(): + zero_all_context_sticks(contexts, console) sdl2.SDL_Delay(1) @@ -990,17 +1163,24 @@ def main() -> None: initialize_sdl(parser) contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] + hotkey_monitor: Optional[ZeroHotkeyMonitor] = None try: if args.list_controllers: list_controllers_with_guids(console, parser) return controller_indices, controller_names = detect_controllers(console, args, parser) pairing = prepare_pairing_state(args, console, parser, controller_indices, controller_names) + if config.zero_hotkey: + candidate = ZeroHotkeyMonitor(config.zero_hotkey, console) + if candidate.start(): + hotkey_monitor = candidate contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console, config) if not contexts: console.print("[yellow]No controllers opened; waiting for hotplug events...[/yellow]") - run_bridge_loop(args, console, config, pairing, contexts, uarts) + run_bridge_loop(args, console, config, pairing, contexts, uarts, hotkey_monitor) finally: + if hotkey_monitor: + hotkey_monitor.stop() cleanup(contexts, uarts) From c37bcc2ce4625441a93a8737ff6e3cd66a51b1c7 Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Sat, 29 Nov 2025 21:03:34 -0700 Subject: [PATCH 067/134] Add swapping hotkey --- README.md | 1 + controller_uart_bridge.py | 212 ++++++++++++++++++++++++++++++++------ 2 files changed, 179 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index e2a9e07..550b994 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Options: - `--swap-abxy` to flip AB/XY globally. - `--swap-abxy-index N` (repeatable) to flip AB/XY for controllers first seen at index N (auto-converts to a stable GUID). - `--swap-abxy-guid GUID` (repeatable) to flip AB/XY for a specific physical controller (GUID is stable across runs). +- `--swap-hotkey x` to pick the runtime hotkey that prompts you to toggle ABXY layout for a specific connected controller (default `x`; empty string disables). - `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). Hot‑plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 33701c2..881ed2d 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -275,65 +275,108 @@ def calibrate_axis_value(value: int, axis: int, ctx: ControllerContext) -> int: return max(-32768, min(32767, value - offset)) -class ZeroHotkeyMonitor: - """Platform-aware helper that watches for a single keypress without blocking the main loop.""" +class HotkeyMonitor: + """Platform-aware helper that watches for configured hotkeys without blocking the main loop.""" - def __init__(self, key: str, console: Console) -> None: - self.key = (key or "").lower() + def __init__(self, console: Console, key_messages: Optional[Dict[str, str]] = None) -> None: self.console = console - self._active = False self._platform = os.name self._fd: Optional[int] = None - self._old_termios = None + self._orig_termios = None self._msvcrt = None + self._active = False + self._started = False + self._keys: Dict[str, str] = {} + if key_messages: + for key, message in key_messages.items(): + self.register_key(key, message) + + def register_key(self, key: str, message: str) -> None: + key = (key or "").lower() + if not key: + return + self._keys[key] = message + + def has_keys(self) -> bool: + return bool(self._keys) def start(self) -> bool: - if not self.key: + if not self._keys or self._started: return False if self._platform == "nt": try: import msvcrt # type: ignore except ImportError: - self.console.print("[yellow]Zero hotkey disabled: msvcrt unavailable.[/yellow]") + self.console.print("[yellow]Hotkeys disabled: msvcrt unavailable.[/yellow]") return False self._msvcrt = msvcrt self._active = True - self.console.print( - f"[magenta]Press '{self.key.upper()}' in this terminal to re-zero controller sticks.[/magenta]" - ) + self._started = True + self._print_instructions() return True if not sys.stdin.isatty(): - self.console.print("[yellow]Zero hotkey disabled: stdin is not a TTY.[/yellow]") + self.console.print("[yellow]Hotkeys disabled: stdin is not a TTY.[/yellow]") return False import termios import tty self._fd = sys.stdin.fileno() - self._old_termios = termios.tcgetattr(self._fd) + self._orig_termios = termios.tcgetattr(self._fd) tty.setcbreak(self._fd) self._active = True - self.console.print( - f"[magenta]Press '{self.key.upper()}' in this terminal to re-zero controller sticks.[/magenta]" - ) + self._started = True + self._print_instructions() return True - def stop(self) -> None: + def suspend(self) -> None: if not self._active: return - if self._platform != "nt" and self._fd is not None and self._old_termios is not None: + if self._platform != "nt" and self._fd is not None and self._orig_termios is not None: import termios - termios.tcsetattr(self._fd, termios.TCSADRAIN, self._old_termios) + termios.tcsetattr(self._fd, termios.TCSADRAIN, self._orig_termios) self._active = False - def poll_trigger(self) -> bool: + def resume(self) -> None: + if not self._started or self._active: + return + if self._platform == "nt": + self._active = True + return + if self._fd is None: + return + import tty + + tty.setcbreak(self._fd) + self._active = True + + def stop(self) -> None: + if self._platform != "nt" and self._fd is not None and self._orig_termios is not None: + import termios + + termios.tcsetattr(self._fd, termios.TCSADRAIN, self._orig_termios) + self._active = False + self._started = False + + def poll_keys(self) -> List[str]: if not self._active: - return False - key = self._read_key() - if key and key.lower() == self.key: - return True - return False + return [] + pressed: List[str] = [] + while True: + key = self._read_key() + if not key: + break + lowered = key.lower() + if lowered in self._keys: + pressed.append(lowered) + return pressed + + def _print_instructions(self) -> None: + if not self._keys: + return + instructions = " | ".join(f"'{key.upper()}' to {message}" for key, message in self._keys.items()) + self.console.print(f"[magenta]Hotkeys active: {instructions}[/magenta]") def _read_key(self) -> Optional[str]: if self._platform == "nt": @@ -373,6 +416,83 @@ def zero_all_context_sticks(contexts: Dict[int, ControllerContext], console: Con zero_context_sticks(ctx, console, reason="Re-zeroed stick centers") +def controller_display_name(ctx: ControllerContext) -> str: + """Return a human-readable controller name.""" + name = sdl2.SDL_GameControllerName(ctx.controller) + if not name: + return "Unknown" + if isinstance(name, bytes): + return name.decode(errors="ignore") + return str(name) + + +def toggle_abxy_for_context(ctx: ControllerContext, config: BridgeConfig, console: Console) -> None: + """Toggle the ABXY layout for a single controller.""" + if config.swap_abxy_global: + console.print("[yellow]Global --swap-abxy is enabled; disable it to use per-controller toggles.[/yellow]") + return + swapped = ctx.stable_id in config.swap_abxy_ids + action = "standard" if swapped else "swapped" + if swapped: + config.swap_abxy_ids.discard(ctx.stable_id) + else: + config.swap_abxy_ids.add(ctx.stable_id) + console.print( + f"[cyan]Controller {ctx.controller_index} ({controller_display_name(ctx)}, inst {ctx.instance_id}) now using {action} ABXY layout.[/cyan]" + ) + + +def prompt_swap_abxy_controller( + contexts: Dict[int, ControllerContext], + config: BridgeConfig, + console: Console, + hotkey: Optional[HotkeyMonitor] = None, +) -> None: + """Prompt the user to choose a controller whose ABXY layout should be toggled.""" + if not contexts: + console.print("[yellow]No controllers connected to toggle ABXY layout.[/yellow]") + return + controllers = sorted(contexts.values(), key=lambda ctx: (ctx.controller_index, ctx.instance_id)) + table = Table(title="Toggle ABXY layout for a controller") + table.add_column("Choice", justify="center") + table.add_column("SDL Index", justify="center") + table.add_column("Instance", justify="center") + table.add_column("Name") + table.add_column("GUID") + table.add_column("Layout", justify="center") + for idx, ctx in enumerate(controllers): + swapped = config.swap_abxy_global or (ctx.stable_id in config.swap_abxy_ids) + state = "Swapped" if swapped else "Standard" + if config.swap_abxy_global: + state += " (global)" + table.add_row( + str(idx), + str(ctx.controller_index), + str(ctx.instance_id), + controller_display_name(ctx), + ctx.stable_id or "unknown", + state, + ) + console.print(table) + choices = [str(i) for i in range(len(controllers))] + ["q"] + if hotkey: + hotkey.suspend() + try: + selection = Prompt.ask( + "Select controller index to toggle ABXY (or 'q' to cancel)", + choices=choices, + default="q", + ) + finally: + if hotkey: + hotkey.resume() + if selection == "q": + console.print("[yellow]ABXY toggle canceled.[/yellow]") + return + ctx = controllers[int(selection)] + toggle_abxy_for_context(ctx, config, console) + + def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int, str]: """Open an SDL GameController by index and return it with instance ID and GUID string.""" controller = sdl2.SDL_GameControllerOpen(index) @@ -458,6 +578,13 @@ def build_arg_parser() -> argparse.ArgumentParser: metavar="KEY", help="Press this key in the terminal to re-zero sticks at runtime (default: 'z', empty string disables).", ) + parser.add_argument( + "--swap-hotkey", + type=parse_hotkey, + default="x", + metavar="KEY", + help="Press this key in the terminal to toggle ABXY layout for a connected controller (default: 'x'; empty string disables).", + ) parser.add_argument( "--trigger-threshold", type=float, @@ -554,10 +681,12 @@ class BridgeConfig: trigger_threshold: int zero_sticks: bool zero_hotkey: str + swap_hotkey: str button_map_default: Dict[int, SwitchButton] button_map_swapped: Dict[int, SwitchButton] swap_abxy_indices: set[int] swap_abxy_ids: set[str] + swap_abxy_global: bool @dataclass @@ -608,10 +737,12 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon trigger_threshold=trigger_threshold, zero_sticks=bool(args.zero_sticks), zero_hotkey=args.zero_hotkey or "", + swap_hotkey=args.swap_hotkey or "", button_map_default=button_map_default, button_map_swapped=button_map_swapped, swap_abxy_indices=swap_abxy_indices, swap_abxy_ids=set(swap_abxy_guids), # filled later once stable IDs are known + swap_abxy_global=bool(args.swap_abxy), ) @@ -930,7 +1061,6 @@ def handle_axis_motion(event: sdl2.SDL_Event, contexts: Dict[int, ControllerCont def handle_button_event( event: sdl2.SDL_Event, - args: argparse.Namespace, config: BridgeConfig, contexts: Dict[int, ControllerContext], ) -> None: @@ -940,7 +1070,7 @@ def handle_button_event( return current_button_map = ( config.button_map_swapped - if (args.swap_abxy or ctx.stable_id in config.swap_abxy_ids) + if (config.swap_abxy_global or ctx.stable_id in config.swap_abxy_ids) else config.button_map_default ) button = event.cbutton.button @@ -1045,7 +1175,7 @@ def service_contexts( for ctx in list(contexts.values()): current_button_map = ( config.button_map_swapped - if (args.swap_abxy or ctx.stable_id in config.swap_abxy_ids) + if (config.swap_abxy_global or ctx.stable_id in config.swap_abxy_ids) else config.button_map_default ) poll_controller_buttons(ctx, current_button_map) @@ -1109,7 +1239,7 @@ def run_bridge_loop( pairing: PairingState, contexts: Dict[int, ControllerContext], uarts: List[PicoUART], - hotkey: Optional[ZeroHotkeyMonitor] = None, + hotkey: Optional[HotkeyMonitor] = None, ) -> None: """Main event loop for bridging controllers to UART and handling rumble.""" event = sdl2.SDL_Event() @@ -1125,7 +1255,7 @@ def run_bridge_loop( if event.type == sdl2.SDL_CONTROLLERAXISMOTION: handle_axis_motion(event, contexts, config) elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): - handle_button_event(event, args, config, contexts) + handle_button_event(event, config, contexts) elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: handle_device_added(event, args, pairing, contexts, uarts, console, config) elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: @@ -1140,8 +1270,12 @@ def run_bridge_loop( else: pair_waiting_contexts(args, pairing, contexts, uarts, console) service_contexts(now, args, config, contexts, uarts, console) - if hotkey and hotkey.poll_trigger(): - zero_all_context_sticks(contexts, console) + if hotkey: + for key in hotkey.poll_keys(): + if key == config.zero_hotkey: + zero_all_context_sticks(contexts, console) + elif key == config.swap_hotkey: + prompt_swap_abxy_controller(contexts, config, console, hotkey) sdl2.SDL_Delay(1) @@ -1163,15 +1297,25 @@ def main() -> None: initialize_sdl(parser) contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] - hotkey_monitor: Optional[ZeroHotkeyMonitor] = None + hotkey_monitor: Optional[HotkeyMonitor] = None try: if args.list_controllers: list_controllers_with_guids(console, parser) return controller_indices, controller_names = detect_controllers(console, args, parser) pairing = prepare_pairing_state(args, console, parser, controller_indices, controller_names) + hotkey_messages: Dict[str, str] = {} if config.zero_hotkey: - candidate = ZeroHotkeyMonitor(config.zero_hotkey, console) + hotkey_messages[config.zero_hotkey] = "re-zero controller sticks" + if config.swap_hotkey: + if config.swap_hotkey in hotkey_messages: + hotkey_messages[config.swap_hotkey] = ( + hotkey_messages[config.swap_hotkey] + "; toggle ABXY layout" + ) + else: + hotkey_messages[config.swap_hotkey] = "toggle ABXY layout for a controller" + if hotkey_messages: + candidate = HotkeyMonitor(console, hotkey_messages) if candidate.start(): hotkey_monitor = candidate contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console, config) From c0c97aa70f24d7b6d4e6cb8b660482c9b285f46f Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Sat, 29 Nov 2025 21:03:34 -0700 Subject: [PATCH 068/134] Add swapping hotkey --- README.md | 1 + controller_uart_bridge.py | 212 ++++++++++++++++++++++++++++++++------ 2 files changed, 179 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index e2a9e07..550b994 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Options: - `--swap-abxy` to flip AB/XY globally. - `--swap-abxy-index N` (repeatable) to flip AB/XY for controllers first seen at index N (auto-converts to a stable GUID). - `--swap-abxy-guid GUID` (repeatable) to flip AB/XY for a specific physical controller (GUID is stable across runs). +- `--swap-hotkey x` to pick the runtime hotkey that prompts you to toggle ABXY layout for a specific connected controller (default `x`; empty string disables). - `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). Hot‑plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 33701c2..881ed2d 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -275,65 +275,108 @@ def calibrate_axis_value(value: int, axis: int, ctx: ControllerContext) -> int: return max(-32768, min(32767, value - offset)) -class ZeroHotkeyMonitor: - """Platform-aware helper that watches for a single keypress without blocking the main loop.""" +class HotkeyMonitor: + """Platform-aware helper that watches for configured hotkeys without blocking the main loop.""" - def __init__(self, key: str, console: Console) -> None: - self.key = (key or "").lower() + def __init__(self, console: Console, key_messages: Optional[Dict[str, str]] = None) -> None: self.console = console - self._active = False self._platform = os.name self._fd: Optional[int] = None - self._old_termios = None + self._orig_termios = None self._msvcrt = None + self._active = False + self._started = False + self._keys: Dict[str, str] = {} + if key_messages: + for key, message in key_messages.items(): + self.register_key(key, message) + + def register_key(self, key: str, message: str) -> None: + key = (key or "").lower() + if not key: + return + self._keys[key] = message + + def has_keys(self) -> bool: + return bool(self._keys) def start(self) -> bool: - if not self.key: + if not self._keys or self._started: return False if self._platform == "nt": try: import msvcrt # type: ignore except ImportError: - self.console.print("[yellow]Zero hotkey disabled: msvcrt unavailable.[/yellow]") + self.console.print("[yellow]Hotkeys disabled: msvcrt unavailable.[/yellow]") return False self._msvcrt = msvcrt self._active = True - self.console.print( - f"[magenta]Press '{self.key.upper()}' in this terminal to re-zero controller sticks.[/magenta]" - ) + self._started = True + self._print_instructions() return True if not sys.stdin.isatty(): - self.console.print("[yellow]Zero hotkey disabled: stdin is not a TTY.[/yellow]") + self.console.print("[yellow]Hotkeys disabled: stdin is not a TTY.[/yellow]") return False import termios import tty self._fd = sys.stdin.fileno() - self._old_termios = termios.tcgetattr(self._fd) + self._orig_termios = termios.tcgetattr(self._fd) tty.setcbreak(self._fd) self._active = True - self.console.print( - f"[magenta]Press '{self.key.upper()}' in this terminal to re-zero controller sticks.[/magenta]" - ) + self._started = True + self._print_instructions() return True - def stop(self) -> None: + def suspend(self) -> None: if not self._active: return - if self._platform != "nt" and self._fd is not None and self._old_termios is not None: + if self._platform != "nt" and self._fd is not None and self._orig_termios is not None: import termios - termios.tcsetattr(self._fd, termios.TCSADRAIN, self._old_termios) + termios.tcsetattr(self._fd, termios.TCSADRAIN, self._orig_termios) self._active = False - def poll_trigger(self) -> bool: + def resume(self) -> None: + if not self._started or self._active: + return + if self._platform == "nt": + self._active = True + return + if self._fd is None: + return + import tty + + tty.setcbreak(self._fd) + self._active = True + + def stop(self) -> None: + if self._platform != "nt" and self._fd is not None and self._orig_termios is not None: + import termios + + termios.tcsetattr(self._fd, termios.TCSADRAIN, self._orig_termios) + self._active = False + self._started = False + + def poll_keys(self) -> List[str]: if not self._active: - return False - key = self._read_key() - if key and key.lower() == self.key: - return True - return False + return [] + pressed: List[str] = [] + while True: + key = self._read_key() + if not key: + break + lowered = key.lower() + if lowered in self._keys: + pressed.append(lowered) + return pressed + + def _print_instructions(self) -> None: + if not self._keys: + return + instructions = " | ".join(f"'{key.upper()}' to {message}" for key, message in self._keys.items()) + self.console.print(f"[magenta]Hotkeys active: {instructions}[/magenta]") def _read_key(self) -> Optional[str]: if self._platform == "nt": @@ -373,6 +416,83 @@ def zero_all_context_sticks(contexts: Dict[int, ControllerContext], console: Con zero_context_sticks(ctx, console, reason="Re-zeroed stick centers") +def controller_display_name(ctx: ControllerContext) -> str: + """Return a human-readable controller name.""" + name = sdl2.SDL_GameControllerName(ctx.controller) + if not name: + return "Unknown" + if isinstance(name, bytes): + return name.decode(errors="ignore") + return str(name) + + +def toggle_abxy_for_context(ctx: ControllerContext, config: BridgeConfig, console: Console) -> None: + """Toggle the ABXY layout for a single controller.""" + if config.swap_abxy_global: + console.print("[yellow]Global --swap-abxy is enabled; disable it to use per-controller toggles.[/yellow]") + return + swapped = ctx.stable_id in config.swap_abxy_ids + action = "standard" if swapped else "swapped" + if swapped: + config.swap_abxy_ids.discard(ctx.stable_id) + else: + config.swap_abxy_ids.add(ctx.stable_id) + console.print( + f"[cyan]Controller {ctx.controller_index} ({controller_display_name(ctx)}, inst {ctx.instance_id}) now using {action} ABXY layout.[/cyan]" + ) + + +def prompt_swap_abxy_controller( + contexts: Dict[int, ControllerContext], + config: BridgeConfig, + console: Console, + hotkey: Optional[HotkeyMonitor] = None, +) -> None: + """Prompt the user to choose a controller whose ABXY layout should be toggled.""" + if not contexts: + console.print("[yellow]No controllers connected to toggle ABXY layout.[/yellow]") + return + controllers = sorted(contexts.values(), key=lambda ctx: (ctx.controller_index, ctx.instance_id)) + table = Table(title="Toggle ABXY layout for a controller") + table.add_column("Choice", justify="center") + table.add_column("SDL Index", justify="center") + table.add_column("Instance", justify="center") + table.add_column("Name") + table.add_column("GUID") + table.add_column("Layout", justify="center") + for idx, ctx in enumerate(controllers): + swapped = config.swap_abxy_global or (ctx.stable_id in config.swap_abxy_ids) + state = "Swapped" if swapped else "Standard" + if config.swap_abxy_global: + state += " (global)" + table.add_row( + str(idx), + str(ctx.controller_index), + str(ctx.instance_id), + controller_display_name(ctx), + ctx.stable_id or "unknown", + state, + ) + console.print(table) + choices = [str(i) for i in range(len(controllers))] + ["q"] + if hotkey: + hotkey.suspend() + try: + selection = Prompt.ask( + "Select controller index to toggle ABXY (or 'q' to cancel)", + choices=choices, + default="q", + ) + finally: + if hotkey: + hotkey.resume() + if selection == "q": + console.print("[yellow]ABXY toggle canceled.[/yellow]") + return + ctx = controllers[int(selection)] + toggle_abxy_for_context(ctx, config, console) + + def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int, str]: """Open an SDL GameController by index and return it with instance ID and GUID string.""" controller = sdl2.SDL_GameControllerOpen(index) @@ -458,6 +578,13 @@ def build_arg_parser() -> argparse.ArgumentParser: metavar="KEY", help="Press this key in the terminal to re-zero sticks at runtime (default: 'z', empty string disables).", ) + parser.add_argument( + "--swap-hotkey", + type=parse_hotkey, + default="x", + metavar="KEY", + help="Press this key in the terminal to toggle ABXY layout for a connected controller (default: 'x'; empty string disables).", + ) parser.add_argument( "--trigger-threshold", type=float, @@ -554,10 +681,12 @@ class BridgeConfig: trigger_threshold: int zero_sticks: bool zero_hotkey: str + swap_hotkey: str button_map_default: Dict[int, SwitchButton] button_map_swapped: Dict[int, SwitchButton] swap_abxy_indices: set[int] swap_abxy_ids: set[str] + swap_abxy_global: bool @dataclass @@ -608,10 +737,12 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon trigger_threshold=trigger_threshold, zero_sticks=bool(args.zero_sticks), zero_hotkey=args.zero_hotkey or "", + swap_hotkey=args.swap_hotkey or "", button_map_default=button_map_default, button_map_swapped=button_map_swapped, swap_abxy_indices=swap_abxy_indices, swap_abxy_ids=set(swap_abxy_guids), # filled later once stable IDs are known + swap_abxy_global=bool(args.swap_abxy), ) @@ -930,7 +1061,6 @@ def handle_axis_motion(event: sdl2.SDL_Event, contexts: Dict[int, ControllerCont def handle_button_event( event: sdl2.SDL_Event, - args: argparse.Namespace, config: BridgeConfig, contexts: Dict[int, ControllerContext], ) -> None: @@ -940,7 +1070,7 @@ def handle_button_event( return current_button_map = ( config.button_map_swapped - if (args.swap_abxy or ctx.stable_id in config.swap_abxy_ids) + if (config.swap_abxy_global or ctx.stable_id in config.swap_abxy_ids) else config.button_map_default ) button = event.cbutton.button @@ -1045,7 +1175,7 @@ def service_contexts( for ctx in list(contexts.values()): current_button_map = ( config.button_map_swapped - if (args.swap_abxy or ctx.stable_id in config.swap_abxy_ids) + if (config.swap_abxy_global or ctx.stable_id in config.swap_abxy_ids) else config.button_map_default ) poll_controller_buttons(ctx, current_button_map) @@ -1109,7 +1239,7 @@ def run_bridge_loop( pairing: PairingState, contexts: Dict[int, ControllerContext], uarts: List[PicoUART], - hotkey: Optional[ZeroHotkeyMonitor] = None, + hotkey: Optional[HotkeyMonitor] = None, ) -> None: """Main event loop for bridging controllers to UART and handling rumble.""" event = sdl2.SDL_Event() @@ -1125,7 +1255,7 @@ def run_bridge_loop( if event.type == sdl2.SDL_CONTROLLERAXISMOTION: handle_axis_motion(event, contexts, config) elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): - handle_button_event(event, args, config, contexts) + handle_button_event(event, config, contexts) elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: handle_device_added(event, args, pairing, contexts, uarts, console, config) elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: @@ -1140,8 +1270,12 @@ def run_bridge_loop( else: pair_waiting_contexts(args, pairing, contexts, uarts, console) service_contexts(now, args, config, contexts, uarts, console) - if hotkey and hotkey.poll_trigger(): - zero_all_context_sticks(contexts, console) + if hotkey: + for key in hotkey.poll_keys(): + if key == config.zero_hotkey: + zero_all_context_sticks(contexts, console) + elif key == config.swap_hotkey: + prompt_swap_abxy_controller(contexts, config, console, hotkey) sdl2.SDL_Delay(1) @@ -1163,15 +1297,25 @@ def main() -> None: initialize_sdl(parser) contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] - hotkey_monitor: Optional[ZeroHotkeyMonitor] = None + hotkey_monitor: Optional[HotkeyMonitor] = None try: if args.list_controllers: list_controllers_with_guids(console, parser) return controller_indices, controller_names = detect_controllers(console, args, parser) pairing = prepare_pairing_state(args, console, parser, controller_indices, controller_names) + hotkey_messages: Dict[str, str] = {} if config.zero_hotkey: - candidate = ZeroHotkeyMonitor(config.zero_hotkey, console) + hotkey_messages[config.zero_hotkey] = "re-zero controller sticks" + if config.swap_hotkey: + if config.swap_hotkey in hotkey_messages: + hotkey_messages[config.swap_hotkey] = ( + hotkey_messages[config.swap_hotkey] + "; toggle ABXY layout" + ) + else: + hotkey_messages[config.swap_hotkey] = "toggle ABXY layout for a controller" + if hotkey_messages: + candidate = HotkeyMonitor(console, hotkey_messages) if candidate.start(): hotkey_monitor = candidate contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console, config) From 3a9f707e9bcf283162bb2b50891fa37b8c00f6bc Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Sat, 29 Nov 2025 21:51:00 -0700 Subject: [PATCH 069/134] Add docs --- README.md | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 550b994..e131173 100644 --- a/README.md +++ b/README.md @@ -5,17 +5,45 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a ## What you get - **Firmware** (`switch-pico.cpp` + `switch_pro_driver.*`): acts as a wired Switch Pro. Takes controller reports over UART1 and passes rumble from the Switch back over UART. - **Python bridge** (`controller_uart_bridge.py`): reads SDL2 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux). -- **Optional Rust bridge** (`fast_uart_bridge/`): higher‑performance alternative if the Python bridge has rumble latency. - **Colour override** (`controller_color_config.h`): compile‑time RGB overrides for body/buttons/grips as seen by the Switch. ## Hardware wiring (Pico) - UART1 pins (fixed in firmware): - - **TX**: GPIO4 (Pico pin 6) → RX of your USB‑serial adapter. - - **RX**: GPIO5 (Pico pin 7) → TX of your USB‑serial adapter. + - **TX**: GPIO4 (Pico pin 6) → RX of your USB-serial adapter. + - **RX**: GPIO5 (Pico pin 7) → TX of your USB-serial adapter. - **GND**: common ground between Pico and adapter. - Baud rate: **921600** (default). Some adapters only handle 500,000; both bridges accept a `--baud` flag. - Keep logic at 3.3V; do not feed 5V UART into the Pico. +### Full hookup checklist +1. **Gather the hardware** + - Raspberry Pi Pico flashed with the provided firmware. + - USB-A-to-micro USB cable (or USB-C if you use a Pico W) to connect the Pico to the Switch or a PC for testing. + - USB-to-UART adapter capable of 3.3 V logic at 921600 baud (FT232, CP2102, CH340, etc.). + - Three dupont wires (TX, RX, GND). Optionally add heat-shrink or a small proto board if you want something more permanent. + +2. **Wire the Pico to the USB-to-UART adapter** + - Pico GPIO4 → adapter RX (sometimes labelled RXD, DI, or R). + - Pico GPIO5 → adapter TX (TXD, DO, or T). + - Pico GND → adapter GND. Tie grounds even if the adapter is already USB-powered. + - Leave VBUS/VCC unconnected unless your adapter explicitly supports 3.3 V power output and you intend to power the Pico from it (the bridge expects the Pico to be powered from USB instead). + +3. **Connect everything to the host and Switch** + - Plug the USB-to-UART adapter into the computer that will run `controller_uart_bridge.py`. Note the COM port (`Device Manager > Ports`) on Windows or `/dev/cu.*`/`/dev/ttyUSB*` path on macOS/Linux; pass it via `--map`/`--ports`. + - Connect the Pico's micro USB port to the Nintendo Switch (via the dock's USB-A port, a USB-C OTG adapter, or a PC if you are only testing). The Pico enumerates as a Switch Pro Controller over USB. + - Any SDL-compatible gamepads you want to use should also be plugged into (or paired with) the same host computer that runs the Python bridge; the bridge is the one reading them. + +4. **Power-on order and sanity checks** + - Power the Switch/dock so the Pico gets 5 V over USB; its USB stack must stay alive while the bridge streams data. + - On the host computer, run `python controller_uart_bridge.py --list-controllers` to make sure SDL sees your pads, then start the bridge with `--map`/`--ports` (or `--interactive`) referencing the adapter path you found earlier. + - Watch the Rich console output: you should see each controller paired with a UART port and the rumble loop logging reconnects if cables are unplugged. + +5. **Common pitfalls** + - A flipped TX/RX pair results in silence (no button presses); swap them if the Pico never shows input. + - Some adapters default to 5 V logic—move the jumper to 3.3 V before touching the Pico. + - If you use multiple adapters, label each cable; COM port numbers can change between boots. + - When testing on a PC before plugging into a Switch, you can verify activity with the lightweight `switch_pico_uart.py` helper or the Windows "Game Controllers" panel. + ## Building and flashing firmware Prereqs: Pico SDK + CMake toolchain set up. ```sh @@ -67,7 +95,13 @@ Options: - `--swap-hotkey x` to pick the runtime hotkey that prompts you to toggle ABXY layout for a specific connected controller (default `x`; empty string disables). - `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). -Hot‑plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. +### Runtime hotkeys +- By default, pressing `z` in the terminal re-samples every connected controller's sticks and re-applies neutral offsets. Change/disable with `--zero-hotkey`. +- Press `x` (configurable via `--swap-hotkey`) to open an in-CLI prompt and toggle the ABXY layout for a specific connected controller. This updates the controller's stable GUID list immediately; press again to revert. +- Hotkeys work only when the bridge is started from a TTY/console that currently has focus. Pass an empty string to either flag to disable that shortcut (useful when running unattended). +- If you launch the bridge with `--swap-abxy` (global swap), the per-controller toggle hotkey will show that the layout is enforced globally and will not override it. + +Hot-plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. ### Using the lightweight UART helper (no SDL needed) For simple scripts or tests you can skip SDL and drive the Pico directly with `switch_pico_uart.py`: From 025dcc4341b4c6ff8859a5a5e728a94907a6cd41 Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Sat, 29 Nov 2025 21:51:00 -0700 Subject: [PATCH 070/134] Add docs --- README.md | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 550b994..e131173 100644 --- a/README.md +++ b/README.md @@ -5,17 +5,45 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a ## What you get - **Firmware** (`switch-pico.cpp` + `switch_pro_driver.*`): acts as a wired Switch Pro. Takes controller reports over UART1 and passes rumble from the Switch back over UART. - **Python bridge** (`controller_uart_bridge.py`): reads SDL2 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux). -- **Optional Rust bridge** (`fast_uart_bridge/`): higher‑performance alternative if the Python bridge has rumble latency. - **Colour override** (`controller_color_config.h`): compile‑time RGB overrides for body/buttons/grips as seen by the Switch. ## Hardware wiring (Pico) - UART1 pins (fixed in firmware): - - **TX**: GPIO4 (Pico pin 6) → RX of your USB‑serial adapter. - - **RX**: GPIO5 (Pico pin 7) → TX of your USB‑serial adapter. + - **TX**: GPIO4 (Pico pin 6) → RX of your USB-serial adapter. + - **RX**: GPIO5 (Pico pin 7) → TX of your USB-serial adapter. - **GND**: common ground between Pico and adapter. - Baud rate: **921600** (default). Some adapters only handle 500,000; both bridges accept a `--baud` flag. - Keep logic at 3.3V; do not feed 5V UART into the Pico. +### Full hookup checklist +1. **Gather the hardware** + - Raspberry Pi Pico flashed with the provided firmware. + - USB-A-to-micro USB cable (or USB-C if you use a Pico W) to connect the Pico to the Switch or a PC for testing. + - USB-to-UART adapter capable of 3.3 V logic at 921600 baud (FT232, CP2102, CH340, etc.). + - Three dupont wires (TX, RX, GND). Optionally add heat-shrink or a small proto board if you want something more permanent. + +2. **Wire the Pico to the USB-to-UART adapter** + - Pico GPIO4 → adapter RX (sometimes labelled RXD, DI, or R). + - Pico GPIO5 → adapter TX (TXD, DO, or T). + - Pico GND → adapter GND. Tie grounds even if the adapter is already USB-powered. + - Leave VBUS/VCC unconnected unless your adapter explicitly supports 3.3 V power output and you intend to power the Pico from it (the bridge expects the Pico to be powered from USB instead). + +3. **Connect everything to the host and Switch** + - Plug the USB-to-UART adapter into the computer that will run `controller_uart_bridge.py`. Note the COM port (`Device Manager > Ports`) on Windows or `/dev/cu.*`/`/dev/ttyUSB*` path on macOS/Linux; pass it via `--map`/`--ports`. + - Connect the Pico's micro USB port to the Nintendo Switch (via the dock's USB-A port, a USB-C OTG adapter, or a PC if you are only testing). The Pico enumerates as a Switch Pro Controller over USB. + - Any SDL-compatible gamepads you want to use should also be plugged into (or paired with) the same host computer that runs the Python bridge; the bridge is the one reading them. + +4. **Power-on order and sanity checks** + - Power the Switch/dock so the Pico gets 5 V over USB; its USB stack must stay alive while the bridge streams data. + - On the host computer, run `python controller_uart_bridge.py --list-controllers` to make sure SDL sees your pads, then start the bridge with `--map`/`--ports` (or `--interactive`) referencing the adapter path you found earlier. + - Watch the Rich console output: you should see each controller paired with a UART port and the rumble loop logging reconnects if cables are unplugged. + +5. **Common pitfalls** + - A flipped TX/RX pair results in silence (no button presses); swap them if the Pico never shows input. + - Some adapters default to 5 V logic—move the jumper to 3.3 V before touching the Pico. + - If you use multiple adapters, label each cable; COM port numbers can change between boots. + - When testing on a PC before plugging into a Switch, you can verify activity with the lightweight `switch_pico_uart.py` helper or the Windows "Game Controllers" panel. + ## Building and flashing firmware Prereqs: Pico SDK + CMake toolchain set up. ```sh @@ -67,7 +95,13 @@ Options: - `--swap-hotkey x` to pick the runtime hotkey that prompts you to toggle ABXY layout for a specific connected controller (default `x`; empty string disables). - `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). -Hot‑plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. +### Runtime hotkeys +- By default, pressing `z` in the terminal re-samples every connected controller's sticks and re-applies neutral offsets. Change/disable with `--zero-hotkey`. +- Press `x` (configurable via `--swap-hotkey`) to open an in-CLI prompt and toggle the ABXY layout for a specific connected controller. This updates the controller's stable GUID list immediately; press again to revert. +- Hotkeys work only when the bridge is started from a TTY/console that currently has focus. Pass an empty string to either flag to disable that shortcut (useful when running unattended). +- If you launch the bridge with `--swap-abxy` (global swap), the per-controller toggle hotkey will show that the layout is enforced globally and will not override it. + +Hot-plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. ### Using the lightweight UART helper (no SDL needed) For simple scripts or tests you can skip SDL and drive the Pico directly with `switch_pico_uart.py`: From abdda5a7c49990d905b2b8c7859e3a649630a9d9 Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Sat, 29 Nov 2025 22:43:02 -0700 Subject: [PATCH 071/134] Add pyproject.toml --- README.md | 25 +++++++++++++++++++++++-- controller_uart_bridge.py | 38 ++++++++++++++++++++++++++++++++++++++ pyproject.toml | 27 +++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 pyproject.toml diff --git a/README.md b/README.md index e131173..bbb7439 100644 --- a/README.md +++ b/README.md @@ -63,17 +63,31 @@ Flags: ## Python bridge (recommended) Works on macOS, Windows, Linux. Uses SDL2 + pyserial. -### Install dependencies with uv (or pip) +### Install dependencies (pyproject-enabled) +The repository now includes a `pyproject.toml`, so you can install the bridge and helper scripts as an editable package: + ```sh # from repo root uv venv .venv -uv pip install pyserial pysdl2 +source .venv/bin/activate # or .venv\Scripts\activate on Windows +uv pip install -e . ``` + +Prefer stock pip? + +```sh +python -m venv .venv +source .venv/bin/activate # or .venv\Scripts\activate on Windows +pip install -e . +``` + - SDL2 runtime: install via your OS package manager (macOS: `brew install sdl2`; Windows: place `SDL2.dll` on PATH or next to the script; Linux: `sudo apt install libsdl2-2.0-0` or equivalent). ### Run ```sh source .venv/bin/activate # or .venv\Scripts\activate on Windows +controller-uart-bridge --interactive +# or, equivalently python controller_uart_bridge.py --interactive ``` Options: @@ -88,6 +102,8 @@ Options: - `--deadzone 0.08` to change stick deadzone (0.0-1.0). - `--zero-sticks` to sample the current stick positions on connect and treat them as neutral (cancel drift). - `--zero-hotkey z` to choose the terminal hotkey that re-zeroes all connected controllers on demand (press `z` by default; pass an empty string to disable). +- `--update-controller-db` to download the latest SDL GameController database before launching (defaults to the copy in `controller_db/` if present). +- `--controller-db-url URL` to override the source URL when updating the controller database (defaults to the official mdqinc repo). - `--trigger-threshold 0.35` to change analog trigger press threshold (0.0-1.0). - `--swap-abxy` to flip AB/XY globally. - `--swap-abxy-index N` (repeatable) to flip AB/XY for controllers first seen at index N (auto-converts to a stable GUID). @@ -101,6 +117,11 @@ Options: - Hotkeys work only when the bridge is started from a TTY/console that currently has focus. Pass an empty string to either flag to disable that shortcut (useful when running unattended). - If you launch the bridge with `--swap-abxy` (global swap), the per-controller toggle hotkey will show that the layout is enforced globally and will not override it. +### Updating SDL controller mappings +- The bridge ships with a pinned `controller_db/gamecontrollerdb.txt`. Run `controller-uart-bridge --update-controller-db ...` to download the latest database from the official upstream (`mdqinc/SDL_GameControllerDB`). +- The download only touches `controller_db/gamecontrollerdb.txt`; add `--controller-db-url https://.../custom.txt` if you maintain your own fork. +- If the file is missing, the bridge will automatically attempt a download on startup. + Hot-plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. ### Using the lightweight UART helper (no SDL needed) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 881ed2d..2e07a58 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -19,6 +19,7 @@ import argparse import os import sys import time +import urllib.request from dataclasses import dataclass, field from ctypes import create_string_buffer from pathlib import Path @@ -50,6 +51,9 @@ RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this RUMBLE_MIN_ACTIVE = 0.50 # below this, rumble is treated as off/noise RUMBLE_SCALE = 0.8 +CONTROLLER_DB_URL_DEFAULT = ( + "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt" +) def parse_mapping(value: str) -> Tuple[int, str]: @@ -66,6 +70,28 @@ def parse_mapping(value: str) -> Tuple[int, str]: return idx, port.strip() +def download_controller_db(console: Console, destination: Path, url: str) -> bool: + """Download the latest SDL controller DB to the local controller_db directory.""" + console.print(f"[cyan]Fetching SDL controller database from {url}...[/cyan]") + try: + with urllib.request.urlopen(url, timeout=20) as response: + status = getattr(response, "status", 200) + if status != 200: + raise RuntimeError(f"HTTP {status}") + data = response.read() + except Exception as exc: + console.print(f"[red]Failed to download controller database: {exc}[/red]") + return False + destination.parent.mkdir(parents=True, exist_ok=True) + try: + destination.write_bytes(data) + except Exception as exc: + console.print(f"[red]Failed to write controller database to {destination}: {exc}[/red]") + return False + console.print(f"[green]Updated controller database ({len(data)} bytes) at {destination}[/green]") + return True + + def parse_hotkey(value: str) -> str: """Validate a single-character hotkey (empty string disables).""" if value is None: @@ -578,6 +604,16 @@ def build_arg_parser() -> argparse.ArgumentParser: metavar="KEY", help="Press this key in the terminal to re-zero sticks at runtime (default: 'z', empty string disables).", ) + parser.add_argument( + "--update-controller-db", + action="store_true", + help="Download the latest SDL GameController database before loading mappings.", + ) + parser.add_argument( + "--controller-db-url", + default=CONTROLLER_DB_URL_DEFAULT, + help="Override the URL used to download the SDL GameController database.", + ) parser.add_argument( "--swap-hotkey", type=parse_hotkey, @@ -704,6 +740,8 @@ class PairingState: def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, SwitchButton], Dict[int, SwitchButton], set[int]]: """Load SDL controller mappings and return button map variants.""" default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" + if args.update_controller_db or not default_mapping.exists(): + download_controller_db(console, default_mapping, args.controller_db_url) mappings_to_load: List[str] = [] if default_mapping.exists(): mappings_to_load.append(str(default_mapping)) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..85f8a54 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[build-system] +requires = ["setuptools>=65"] +build-backend = "setuptools.build_meta" + +[project] +name = "switch-pico-bridge" +version = "0.1.0" +description = "SDL2-to-UART host bridge and helpers for the switch-pico firmware." +readme = "README.md" +requires-python = ">=3.9" +authors = [{ name = "Switch Pico Maintainers" }] +dependencies = [ + "pyserial", + "pysdl2", + "rich", +] + +[project.scripts] +controller-uart-bridge = "controller_uart_bridge:main" +host-uart-logger = "host_uart_logger:main" + +[tool.setuptools] +py-modules = [ + "controller_uart_bridge", + "host_uart_logger", + "switch_pico_uart", +] From 5086fdaa03eb93d5b8cca9c3a8f672cefa37edac Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Sat, 29 Nov 2025 22:43:02 -0700 Subject: [PATCH 072/134] Add pyproject.toml --- README.md | 25 +++++++++++++++++++++++-- controller_uart_bridge.py | 38 ++++++++++++++++++++++++++++++++++++++ pyproject.toml | 27 +++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 pyproject.toml diff --git a/README.md b/README.md index e131173..bbb7439 100644 --- a/README.md +++ b/README.md @@ -63,17 +63,31 @@ Flags: ## Python bridge (recommended) Works on macOS, Windows, Linux. Uses SDL2 + pyserial. -### Install dependencies with uv (or pip) +### Install dependencies (pyproject-enabled) +The repository now includes a `pyproject.toml`, so you can install the bridge and helper scripts as an editable package: + ```sh # from repo root uv venv .venv -uv pip install pyserial pysdl2 +source .venv/bin/activate # or .venv\Scripts\activate on Windows +uv pip install -e . ``` + +Prefer stock pip? + +```sh +python -m venv .venv +source .venv/bin/activate # or .venv\Scripts\activate on Windows +pip install -e . +``` + - SDL2 runtime: install via your OS package manager (macOS: `brew install sdl2`; Windows: place `SDL2.dll` on PATH or next to the script; Linux: `sudo apt install libsdl2-2.0-0` or equivalent). ### Run ```sh source .venv/bin/activate # or .venv\Scripts\activate on Windows +controller-uart-bridge --interactive +# or, equivalently python controller_uart_bridge.py --interactive ``` Options: @@ -88,6 +102,8 @@ Options: - `--deadzone 0.08` to change stick deadzone (0.0-1.0). - `--zero-sticks` to sample the current stick positions on connect and treat them as neutral (cancel drift). - `--zero-hotkey z` to choose the terminal hotkey that re-zeroes all connected controllers on demand (press `z` by default; pass an empty string to disable). +- `--update-controller-db` to download the latest SDL GameController database before launching (defaults to the copy in `controller_db/` if present). +- `--controller-db-url URL` to override the source URL when updating the controller database (defaults to the official mdqinc repo). - `--trigger-threshold 0.35` to change analog trigger press threshold (0.0-1.0). - `--swap-abxy` to flip AB/XY globally. - `--swap-abxy-index N` (repeatable) to flip AB/XY for controllers first seen at index N (auto-converts to a stable GUID). @@ -101,6 +117,11 @@ Options: - Hotkeys work only when the bridge is started from a TTY/console that currently has focus. Pass an empty string to either flag to disable that shortcut (useful when running unattended). - If you launch the bridge with `--swap-abxy` (global swap), the per-controller toggle hotkey will show that the layout is enforced globally and will not override it. +### Updating SDL controller mappings +- The bridge ships with a pinned `controller_db/gamecontrollerdb.txt`. Run `controller-uart-bridge --update-controller-db ...` to download the latest database from the official upstream (`mdqinc/SDL_GameControllerDB`). +- The download only touches `controller_db/gamecontrollerdb.txt`; add `--controller-db-url https://.../custom.txt` if you maintain your own fork. +- If the file is missing, the bridge will automatically attempt a download on startup. + Hot-plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. ### Using the lightweight UART helper (no SDL needed) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 881ed2d..2e07a58 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -19,6 +19,7 @@ import argparse import os import sys import time +import urllib.request from dataclasses import dataclass, field from ctypes import create_string_buffer from pathlib import Path @@ -50,6 +51,9 @@ RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this RUMBLE_MIN_ACTIVE = 0.50 # below this, rumble is treated as off/noise RUMBLE_SCALE = 0.8 +CONTROLLER_DB_URL_DEFAULT = ( + "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt" +) def parse_mapping(value: str) -> Tuple[int, str]: @@ -66,6 +70,28 @@ def parse_mapping(value: str) -> Tuple[int, str]: return idx, port.strip() +def download_controller_db(console: Console, destination: Path, url: str) -> bool: + """Download the latest SDL controller DB to the local controller_db directory.""" + console.print(f"[cyan]Fetching SDL controller database from {url}...[/cyan]") + try: + with urllib.request.urlopen(url, timeout=20) as response: + status = getattr(response, "status", 200) + if status != 200: + raise RuntimeError(f"HTTP {status}") + data = response.read() + except Exception as exc: + console.print(f"[red]Failed to download controller database: {exc}[/red]") + return False + destination.parent.mkdir(parents=True, exist_ok=True) + try: + destination.write_bytes(data) + except Exception as exc: + console.print(f"[red]Failed to write controller database to {destination}: {exc}[/red]") + return False + console.print(f"[green]Updated controller database ({len(data)} bytes) at {destination}[/green]") + return True + + def parse_hotkey(value: str) -> str: """Validate a single-character hotkey (empty string disables).""" if value is None: @@ -578,6 +604,16 @@ def build_arg_parser() -> argparse.ArgumentParser: metavar="KEY", help="Press this key in the terminal to re-zero sticks at runtime (default: 'z', empty string disables).", ) + parser.add_argument( + "--update-controller-db", + action="store_true", + help="Download the latest SDL GameController database before loading mappings.", + ) + parser.add_argument( + "--controller-db-url", + default=CONTROLLER_DB_URL_DEFAULT, + help="Override the URL used to download the SDL GameController database.", + ) parser.add_argument( "--swap-hotkey", type=parse_hotkey, @@ -704,6 +740,8 @@ class PairingState: def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, SwitchButton], Dict[int, SwitchButton], set[int]]: """Load SDL controller mappings and return button map variants.""" default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" + if args.update_controller_db or not default_mapping.exists(): + download_controller_db(console, default_mapping, args.controller_db_url) mappings_to_load: List[str] = [] if default_mapping.exists(): mappings_to_load.append(str(default_mapping)) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..85f8a54 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[build-system] +requires = ["setuptools>=65"] +build-backend = "setuptools.build_meta" + +[project] +name = "switch-pico-bridge" +version = "0.1.0" +description = "SDL2-to-UART host bridge and helpers for the switch-pico firmware." +readme = "README.md" +requires-python = ">=3.9" +authors = [{ name = "Switch Pico Maintainers" }] +dependencies = [ + "pyserial", + "pysdl2", + "rich", +] + +[project.scripts] +controller-uart-bridge = "controller_uart_bridge:main" +host-uart-logger = "host_uart_logger:main" + +[tool.setuptools] +py-modules = [ + "controller_uart_bridge", + "host_uart_logger", + "switch_pico_uart", +] From 1cb7075180fe4e1d9645a9401fa2dd0dc2596899 Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Mon, 1 Dec 2025 12:45:13 -0700 Subject: [PATCH 073/134] Make example work and redo some library code --- README.md | 6 +- controller_uart_bridge.py | 8 +-- example_switch_macro.py | 58 ++++++++++++++++ switch_pico_uart.py | 141 +++++++++++++++++++++++++++++--------- 4 files changed, 172 insertions(+), 41 deletions(-) create mode 100644 example_switch_macro.py diff --git a/README.md b/README.md index bbb7439..e7f5626 100644 --- a/README.md +++ b/README.md @@ -127,16 +127,16 @@ Hot-plugging: controllers and UARTs can be plugged/unplugged while running; the ### Using the lightweight UART helper (no SDL needed) For simple scripts or tests you can skip SDL and drive the Pico directly with `switch_pico_uart.py`: ```python -from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchHat +from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchDpad with SwitchUARTClient("/dev/cu.usbserial-0001") as client: client.press(SwitchButton.A) client.release(SwitchButton.A) client.move_left_stick(0.0, -1.0) # push up - client.set_hat(SwitchHat.TOP_RIGHT) + client.set_hat(SwitchDpad.UP_RIGHT) print(client.poll_rumble()) # returns (left, right) amplitudes 0.0-1.0 or None ``` -- `SwitchButton` is an `IntFlag` (bitwise friendly) and `SwitchHat` is an `IntEnum` for the DPAD/hat values. +- `SwitchButton` is an `IntFlag` (bitwise friendly) and `SwitchDpad` is an `IntEnum` for the DPAD/hat values (alias `SwitchHat` remains for older scripts). - The helper only depends on `pyserial`; SDL is not required. ### macOS tips diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 2e07a58..3b4c50e 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -39,10 +39,10 @@ from switch_pico_uart import ( UART_BAUD, PicoUART, SwitchButton, - SwitchHat, + SwitchDpad, SwitchReport, axis_to_stick, - dpad_to_hat, + str_to_dpad, decode_rumble, trigger_to_button, ) @@ -707,7 +707,7 @@ def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, Switch dpad_changed = True if dpad_changed: - ctx.report.hat = dpad_to_hat(ctx.dpad) + ctx.report.hat = str_to_dpad(ctx.dpad) @dataclass @@ -1122,7 +1122,7 @@ def handle_button_event( ctx.button_state[button] = pressed elif button in DPAD_BUTTONS: ctx.dpad[DPAD_BUTTONS[button]] = pressed - ctx.report.hat = dpad_to_hat(ctx.dpad) + ctx.report.hat = str_to_dpad(ctx.dpad) def handle_device_added( diff --git a/example_switch_macro.py b/example_switch_macro.py new file mode 100644 index 0000000..deadba7 --- /dev/null +++ b/example_switch_macro.py @@ -0,0 +1,58 @@ +# example_switch_macro.py +import time +from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchDpad + +PORT = "COM5" # change to your serial port, e.g. /dev/cu.usbserial-0001 +SEND_INTERVAL = 1 / 500 # optional: match controller_uart_bridge default + +# Convenience list of every SwitchButton (plus DPAD directions). +ALL_BUTTONS = [ + SwitchButton.A, + SwitchButton.B, + SwitchButton.X, + SwitchButton.Y, + SwitchButton.L, + SwitchButton.R, + SwitchButton.ZL, + SwitchButton.ZR, + SwitchButton.PLUS, + SwitchButton.MINUS, + SwitchButton.CAPTURE, + SwitchButton.LCLICK, + SwitchButton.RCLICK, + SwitchDpad.DOWN, + SwitchDpad.UP, + SwitchDpad.LEFT, + SwitchDpad.RIGHT, +] + + +def main() -> None: + # auto_send keeps the current state flowing in the background, so we don't + # need to manually pump frames to the Pico. + with SwitchUARTClient(PORT, send_interval=SEND_INTERVAL, auto_send=True) as client: + client.neutral() + + # Press every button/DPAD direction one-by-one, holding each briefly. + for button in ALL_BUTTONS: + client.press_for(0.10, button) + time.sleep(0.05) # short gap between presses + + # Push left stick up briefly. + client.move_left_stick_for(0.0, -1.0, 0.2) + + # Hold dpad right for one second using hat-friendly press/release. + client.press_for(0.5, SwitchDpad.RIGHT) + + # Listen for rumble frames for a few seconds while the background sender runs. + end = time.monotonic() + 3 + while time.monotonic() < end: + rumble = client.poll_rumble() + if rumble: + left, right = rumble + print(f"Rumble: L={left:.2f} R={right:.2f}") + time.sleep(0.01) + + +if __name__ == "__main__": + main() diff --git a/switch_pico_uart.py b/switch_pico_uart.py index be49d42..00ccb98 100644 --- a/switch_pico_uart.py +++ b/switch_pico_uart.py @@ -14,6 +14,7 @@ from __future__ import annotations import struct import time +import threading from dataclasses import dataclass, field from enum import IntEnum, IntFlag from typing import Iterable, Mapping, Optional, Tuple, Union @@ -44,15 +45,15 @@ class SwitchButton(IntFlag): CAPTURE = 1 << 13 -class SwitchHat(IntEnum): - TOP = 0x00 - TOP_RIGHT = 0x01 +class SwitchDpad(IntEnum): + UP = 0x00 + UP_RIGHT = 0x01 RIGHT = 0x02 - BOTTOM_RIGHT = 0x03 - BOTTOM = 0x04 - BOTTOM_LEFT = 0x05 + DOWN_RIGHT = 0x03 + DOWN = 0x04 + DOWN_LEFT = 0x05 LEFT = 0x06 - TOP_LEFT = 0x07 + UP_LEFT = 0x07 CENTER = 0x08 @@ -88,7 +89,7 @@ def trigger_to_button(value: int, threshold: int) -> bool: return value >= threshold -def dpad_to_hat(flags: Mapping[str, bool]) -> SwitchHat: +def str_to_dpad(flags: Mapping[str, bool]) -> SwitchDpad: """Translate DPAD button flags into a Switch hat value.""" up = flags.get("up", False) down = flags.get("down", False) @@ -96,28 +97,28 @@ def dpad_to_hat(flags: Mapping[str, bool]) -> SwitchHat: right = flags.get("right", False) if up and right: - return SwitchHat.TOP_RIGHT + return SwitchDpad.UP_RIGHT if up and left: - return SwitchHat.TOP_LEFT + return SwitchDpad.UP_LEFT if down and right: - return SwitchHat.BOTTOM_RIGHT + return SwitchDpad.DOWN_RIGHT if down and left: - return SwitchHat.BOTTOM_LEFT + return SwitchDpad.DOWN_LEFT if up: - return SwitchHat.TOP + return SwitchDpad.UP if down: - return SwitchHat.BOTTOM + return SwitchDpad.DOWN if right: - return SwitchHat.RIGHT + return SwitchDpad.RIGHT if left: - return SwitchHat.LEFT - return SwitchHat.CENTER + return SwitchDpad.LEFT + return SwitchDpad.CENTER @dataclass class SwitchReport: buttons: int = 0 - hat: SwitchHat = SwitchHat.CENTER + hat: SwitchDpad = SwitchDpad.CENTER lx: int = 128 ly: int = 128 rx: int = 128 @@ -215,24 +216,31 @@ class SwitchControllerState: report: SwitchReport = field(default_factory=SwitchReport) - def press(self, *buttons: Union[SwitchButton, int]) -> None: - """Set one or more buttons as pressed.""" - for button in buttons: - self.report.buttons |= int(button) + def press(self, *buttons_or_hat: Union[SwitchButton, SwitchDpad, int]) -> None: + """Press one or more buttons, or set the hat if a SwitchDpad is provided.""" + for item in buttons_or_hat: + if isinstance(item, SwitchDpad): + # If multiple hats are provided, the last one wins. + self.report.hat = SwitchDpad(int(item) & 0xFF) + else: + self.report.buttons |= int(item) - def release(self, *buttons: Union[SwitchButton, int]) -> None: - """Release one or more buttons.""" - for button in buttons: - self.report.buttons &= ~int(button) + def release(self, *buttons_or_hat: Union[SwitchButton, SwitchDpad, int]) -> None: + """Release one or more buttons, or center the hat if a SwitchDpad is provided.""" + for item in buttons_or_hat: + if isinstance(item, SwitchDpad): + self.report.hat = SwitchDpad.CENTER + else: + self.report.buttons &= ~int(item) def set_buttons(self, buttons: Iterable[Union[SwitchButton, int]]) -> None: """Replace the current button bitmask with the provided buttons.""" self.report.buttons = 0 self.press(*buttons) - def set_hat(self, hat: Union[SwitchHat, int]) -> None: + def set_hat(self, hat: Union[SwitchDpad, int]) -> None: """Set the DPAD/hat value directly.""" - self.report.hat = int(hat) & 0xFF + self.report.hat = SwitchDpad(int(hat) & 0xFF) def move_left_stick(self, x: Union[int, float], y: Union[int, float]) -> None: """Move the left stick using normalized floats (-1..1) or raw bytes (0-255).""" @@ -247,7 +255,7 @@ class SwitchControllerState: def neutral(self) -> None: """Clear all input back to the neutral controller state.""" self.report.buttons = 0 - self.report.hat = SwitchHat.CENTER + self.report.hat = SwitchDpad.CENTER self.report.lx = 128 self.report.ly = 128 self.report.rx = 128 @@ -266,11 +274,30 @@ class SwitchUARTClient: client.move_left_stick(0.0, -1.0) # push up """ - def __init__(self, port: str, baud: int = UART_BAUD, send_interval: float = 0.0) -> None: + def __init__( + self, + port: str, + baud: int = UART_BAUD, + send_interval: float = 1.0 / 500.0, + auto_send: bool = True, + ) -> None: + """ + Args: + port: Serial port path (e.g., 'COM5' or '/dev/cu.usbserial-0001'). + baud: UART baud rate. + send_interval: Minimum interval between sends in seconds (defaults to 500 Hz). + auto_send: If True, keep sending the current state in a background thread so the + Pico continuously sees the latest input (mirrors controller_uart_bridge). + """ self.uart = PicoUART(port, baud) self.state = SwitchControllerState() self.send_interval = max(0.0, send_interval) self._last_send = 0.0 + self._auto_send = auto_send + self._stop_event = threading.Event() + self._auto_thread: Optional[threading.Thread] = None + if self._auto_send: + self._start_auto_send_thread() def send(self) -> None: """Send the current state to the Pico, throttled by send_interval if set.""" @@ -280,19 +307,35 @@ class SwitchUARTClient: self.uart.send_report(self.state.report) self._last_send = now - def press(self, *buttons: int) -> None: + def _start_auto_send_thread(self) -> None: + """Continuously send the current state so the Pico stays active.""" + if self._auto_thread is not None: + return + sleep_time = self.send_interval if self.send_interval > 0 else 0.002 + + def loop() -> None: + while not self._stop_event.is_set(): + self.send() + self._stop_event.wait(sleep_time) + + self._auto_thread = threading.Thread(target=loop, daemon=True) + self._auto_thread.start() + + def press(self, *buttons: SwitchButton | SwitchDpad | int) -> None: + """Press buttons or set hat using SwitchButton/SwitchDpad (ints also allowed).""" self.state.press(*buttons) self.send() - def release(self, *buttons: int) -> None: + def release(self, *buttons: SwitchButton | SwitchDpad | int) -> None: + """Release buttons or center hat when given a SwitchDpad.""" self.state.release(*buttons) self.send() - def set_buttons(self, buttons: Iterable[int]) -> None: + def set_buttons(self, buttons: Iterable[SwitchButton | int]) -> None: self.state.set_buttons(buttons) self.send() - def set_hat(self, hat: int) -> None: + def set_hat(self, hat: SwitchDpad | int) -> None: self.state.set_hat(hat) self.send() @@ -304,6 +347,32 @@ class SwitchUARTClient: self.state.move_right_stick(x, y) self.send() + def press_for(self, duration: float, *buttons: SwitchButton | SwitchDpad | int) -> None: + """Press buttons/hat for a duration, then release.""" + self.press(*buttons) + time.sleep(max(0.0, duration)) + self.release(*buttons) + + def move_left_stick_for( + self, x: Union[int, float], y: Union[int, float], duration: float, neutral_after: bool = True + ) -> None: + """Move left stick for a duration, optionally returning it to neutral afterward.""" + self.move_left_stick(x, y) + time.sleep(max(0.0, duration)) + if neutral_after: + self.state.move_left_stick(128, 128) + self.send() + + def move_right_stick_for( + self, x: Union[int, float], y: Union[int, float], duration: float, neutral_after: bool = True + ) -> None: + """Move right stick for a duration, optionally returning it to neutral afterward.""" + self.move_right_stick(x, y) + time.sleep(max(0.0, duration)) + if neutral_after: + self.state.move_right_stick(128, 128) + self.send() + def neutral(self) -> None: self.state.neutral() self.send() @@ -319,6 +388,10 @@ class SwitchUARTClient: return None def close(self) -> None: + if self._auto_thread: + self._stop_event.set() + self._auto_thread.join(timeout=0.5) + self._auto_thread = None self.uart.close() def __enter__(self) -> "SwitchUARTClient": From 672039010968ad3714ae31526cd75ac18b7ed442 Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Mon, 1 Dec 2025 12:45:13 -0700 Subject: [PATCH 074/134] Make example work and redo some library code --- README.md | 6 +- controller_uart_bridge.py | 8 +-- example_switch_macro.py | 58 ++++++++++++++++ switch_pico_uart.py | 141 +++++++++++++++++++++++++++++--------- 4 files changed, 172 insertions(+), 41 deletions(-) create mode 100644 example_switch_macro.py diff --git a/README.md b/README.md index bbb7439..e7f5626 100644 --- a/README.md +++ b/README.md @@ -127,16 +127,16 @@ Hot-plugging: controllers and UARTs can be plugged/unplugged while running; the ### Using the lightweight UART helper (no SDL needed) For simple scripts or tests you can skip SDL and drive the Pico directly with `switch_pico_uart.py`: ```python -from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchHat +from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchDpad with SwitchUARTClient("/dev/cu.usbserial-0001") as client: client.press(SwitchButton.A) client.release(SwitchButton.A) client.move_left_stick(0.0, -1.0) # push up - client.set_hat(SwitchHat.TOP_RIGHT) + client.set_hat(SwitchDpad.UP_RIGHT) print(client.poll_rumble()) # returns (left, right) amplitudes 0.0-1.0 or None ``` -- `SwitchButton` is an `IntFlag` (bitwise friendly) and `SwitchHat` is an `IntEnum` for the DPAD/hat values. +- `SwitchButton` is an `IntFlag` (bitwise friendly) and `SwitchDpad` is an `IntEnum` for the DPAD/hat values (alias `SwitchHat` remains for older scripts). - The helper only depends on `pyserial`; SDL is not required. ### macOS tips diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 2e07a58..3b4c50e 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -39,10 +39,10 @@ from switch_pico_uart import ( UART_BAUD, PicoUART, SwitchButton, - SwitchHat, + SwitchDpad, SwitchReport, axis_to_stick, - dpad_to_hat, + str_to_dpad, decode_rumble, trigger_to_button, ) @@ -707,7 +707,7 @@ def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, Switch dpad_changed = True if dpad_changed: - ctx.report.hat = dpad_to_hat(ctx.dpad) + ctx.report.hat = str_to_dpad(ctx.dpad) @dataclass @@ -1122,7 +1122,7 @@ def handle_button_event( ctx.button_state[button] = pressed elif button in DPAD_BUTTONS: ctx.dpad[DPAD_BUTTONS[button]] = pressed - ctx.report.hat = dpad_to_hat(ctx.dpad) + ctx.report.hat = str_to_dpad(ctx.dpad) def handle_device_added( diff --git a/example_switch_macro.py b/example_switch_macro.py new file mode 100644 index 0000000..deadba7 --- /dev/null +++ b/example_switch_macro.py @@ -0,0 +1,58 @@ +# example_switch_macro.py +import time +from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchDpad + +PORT = "COM5" # change to your serial port, e.g. /dev/cu.usbserial-0001 +SEND_INTERVAL = 1 / 500 # optional: match controller_uart_bridge default + +# Convenience list of every SwitchButton (plus DPAD directions). +ALL_BUTTONS = [ + SwitchButton.A, + SwitchButton.B, + SwitchButton.X, + SwitchButton.Y, + SwitchButton.L, + SwitchButton.R, + SwitchButton.ZL, + SwitchButton.ZR, + SwitchButton.PLUS, + SwitchButton.MINUS, + SwitchButton.CAPTURE, + SwitchButton.LCLICK, + SwitchButton.RCLICK, + SwitchDpad.DOWN, + SwitchDpad.UP, + SwitchDpad.LEFT, + SwitchDpad.RIGHT, +] + + +def main() -> None: + # auto_send keeps the current state flowing in the background, so we don't + # need to manually pump frames to the Pico. + with SwitchUARTClient(PORT, send_interval=SEND_INTERVAL, auto_send=True) as client: + client.neutral() + + # Press every button/DPAD direction one-by-one, holding each briefly. + for button in ALL_BUTTONS: + client.press_for(0.10, button) + time.sleep(0.05) # short gap between presses + + # Push left stick up briefly. + client.move_left_stick_for(0.0, -1.0, 0.2) + + # Hold dpad right for one second using hat-friendly press/release. + client.press_for(0.5, SwitchDpad.RIGHT) + + # Listen for rumble frames for a few seconds while the background sender runs. + end = time.monotonic() + 3 + while time.monotonic() < end: + rumble = client.poll_rumble() + if rumble: + left, right = rumble + print(f"Rumble: L={left:.2f} R={right:.2f}") + time.sleep(0.01) + + +if __name__ == "__main__": + main() diff --git a/switch_pico_uart.py b/switch_pico_uart.py index be49d42..00ccb98 100644 --- a/switch_pico_uart.py +++ b/switch_pico_uart.py @@ -14,6 +14,7 @@ from __future__ import annotations import struct import time +import threading from dataclasses import dataclass, field from enum import IntEnum, IntFlag from typing import Iterable, Mapping, Optional, Tuple, Union @@ -44,15 +45,15 @@ class SwitchButton(IntFlag): CAPTURE = 1 << 13 -class SwitchHat(IntEnum): - TOP = 0x00 - TOP_RIGHT = 0x01 +class SwitchDpad(IntEnum): + UP = 0x00 + UP_RIGHT = 0x01 RIGHT = 0x02 - BOTTOM_RIGHT = 0x03 - BOTTOM = 0x04 - BOTTOM_LEFT = 0x05 + DOWN_RIGHT = 0x03 + DOWN = 0x04 + DOWN_LEFT = 0x05 LEFT = 0x06 - TOP_LEFT = 0x07 + UP_LEFT = 0x07 CENTER = 0x08 @@ -88,7 +89,7 @@ def trigger_to_button(value: int, threshold: int) -> bool: return value >= threshold -def dpad_to_hat(flags: Mapping[str, bool]) -> SwitchHat: +def str_to_dpad(flags: Mapping[str, bool]) -> SwitchDpad: """Translate DPAD button flags into a Switch hat value.""" up = flags.get("up", False) down = flags.get("down", False) @@ -96,28 +97,28 @@ def dpad_to_hat(flags: Mapping[str, bool]) -> SwitchHat: right = flags.get("right", False) if up and right: - return SwitchHat.TOP_RIGHT + return SwitchDpad.UP_RIGHT if up and left: - return SwitchHat.TOP_LEFT + return SwitchDpad.UP_LEFT if down and right: - return SwitchHat.BOTTOM_RIGHT + return SwitchDpad.DOWN_RIGHT if down and left: - return SwitchHat.BOTTOM_LEFT + return SwitchDpad.DOWN_LEFT if up: - return SwitchHat.TOP + return SwitchDpad.UP if down: - return SwitchHat.BOTTOM + return SwitchDpad.DOWN if right: - return SwitchHat.RIGHT + return SwitchDpad.RIGHT if left: - return SwitchHat.LEFT - return SwitchHat.CENTER + return SwitchDpad.LEFT + return SwitchDpad.CENTER @dataclass class SwitchReport: buttons: int = 0 - hat: SwitchHat = SwitchHat.CENTER + hat: SwitchDpad = SwitchDpad.CENTER lx: int = 128 ly: int = 128 rx: int = 128 @@ -215,24 +216,31 @@ class SwitchControllerState: report: SwitchReport = field(default_factory=SwitchReport) - def press(self, *buttons: Union[SwitchButton, int]) -> None: - """Set one or more buttons as pressed.""" - for button in buttons: - self.report.buttons |= int(button) + def press(self, *buttons_or_hat: Union[SwitchButton, SwitchDpad, int]) -> None: + """Press one or more buttons, or set the hat if a SwitchDpad is provided.""" + for item in buttons_or_hat: + if isinstance(item, SwitchDpad): + # If multiple hats are provided, the last one wins. + self.report.hat = SwitchDpad(int(item) & 0xFF) + else: + self.report.buttons |= int(item) - def release(self, *buttons: Union[SwitchButton, int]) -> None: - """Release one or more buttons.""" - for button in buttons: - self.report.buttons &= ~int(button) + def release(self, *buttons_or_hat: Union[SwitchButton, SwitchDpad, int]) -> None: + """Release one or more buttons, or center the hat if a SwitchDpad is provided.""" + for item in buttons_or_hat: + if isinstance(item, SwitchDpad): + self.report.hat = SwitchDpad.CENTER + else: + self.report.buttons &= ~int(item) def set_buttons(self, buttons: Iterable[Union[SwitchButton, int]]) -> None: """Replace the current button bitmask with the provided buttons.""" self.report.buttons = 0 self.press(*buttons) - def set_hat(self, hat: Union[SwitchHat, int]) -> None: + def set_hat(self, hat: Union[SwitchDpad, int]) -> None: """Set the DPAD/hat value directly.""" - self.report.hat = int(hat) & 0xFF + self.report.hat = SwitchDpad(int(hat) & 0xFF) def move_left_stick(self, x: Union[int, float], y: Union[int, float]) -> None: """Move the left stick using normalized floats (-1..1) or raw bytes (0-255).""" @@ -247,7 +255,7 @@ class SwitchControllerState: def neutral(self) -> None: """Clear all input back to the neutral controller state.""" self.report.buttons = 0 - self.report.hat = SwitchHat.CENTER + self.report.hat = SwitchDpad.CENTER self.report.lx = 128 self.report.ly = 128 self.report.rx = 128 @@ -266,11 +274,30 @@ class SwitchUARTClient: client.move_left_stick(0.0, -1.0) # push up """ - def __init__(self, port: str, baud: int = UART_BAUD, send_interval: float = 0.0) -> None: + def __init__( + self, + port: str, + baud: int = UART_BAUD, + send_interval: float = 1.0 / 500.0, + auto_send: bool = True, + ) -> None: + """ + Args: + port: Serial port path (e.g., 'COM5' or '/dev/cu.usbserial-0001'). + baud: UART baud rate. + send_interval: Minimum interval between sends in seconds (defaults to 500 Hz). + auto_send: If True, keep sending the current state in a background thread so the + Pico continuously sees the latest input (mirrors controller_uart_bridge). + """ self.uart = PicoUART(port, baud) self.state = SwitchControllerState() self.send_interval = max(0.0, send_interval) self._last_send = 0.0 + self._auto_send = auto_send + self._stop_event = threading.Event() + self._auto_thread: Optional[threading.Thread] = None + if self._auto_send: + self._start_auto_send_thread() def send(self) -> None: """Send the current state to the Pico, throttled by send_interval if set.""" @@ -280,19 +307,35 @@ class SwitchUARTClient: self.uart.send_report(self.state.report) self._last_send = now - def press(self, *buttons: int) -> None: + def _start_auto_send_thread(self) -> None: + """Continuously send the current state so the Pico stays active.""" + if self._auto_thread is not None: + return + sleep_time = self.send_interval if self.send_interval > 0 else 0.002 + + def loop() -> None: + while not self._stop_event.is_set(): + self.send() + self._stop_event.wait(sleep_time) + + self._auto_thread = threading.Thread(target=loop, daemon=True) + self._auto_thread.start() + + def press(self, *buttons: SwitchButton | SwitchDpad | int) -> None: + """Press buttons or set hat using SwitchButton/SwitchDpad (ints also allowed).""" self.state.press(*buttons) self.send() - def release(self, *buttons: int) -> None: + def release(self, *buttons: SwitchButton | SwitchDpad | int) -> None: + """Release buttons or center hat when given a SwitchDpad.""" self.state.release(*buttons) self.send() - def set_buttons(self, buttons: Iterable[int]) -> None: + def set_buttons(self, buttons: Iterable[SwitchButton | int]) -> None: self.state.set_buttons(buttons) self.send() - def set_hat(self, hat: int) -> None: + def set_hat(self, hat: SwitchDpad | int) -> None: self.state.set_hat(hat) self.send() @@ -304,6 +347,32 @@ class SwitchUARTClient: self.state.move_right_stick(x, y) self.send() + def press_for(self, duration: float, *buttons: SwitchButton | SwitchDpad | int) -> None: + """Press buttons/hat for a duration, then release.""" + self.press(*buttons) + time.sleep(max(0.0, duration)) + self.release(*buttons) + + def move_left_stick_for( + self, x: Union[int, float], y: Union[int, float], duration: float, neutral_after: bool = True + ) -> None: + """Move left stick for a duration, optionally returning it to neutral afterward.""" + self.move_left_stick(x, y) + time.sleep(max(0.0, duration)) + if neutral_after: + self.state.move_left_stick(128, 128) + self.send() + + def move_right_stick_for( + self, x: Union[int, float], y: Union[int, float], duration: float, neutral_after: bool = True + ) -> None: + """Move right stick for a duration, optionally returning it to neutral afterward.""" + self.move_right_stick(x, y) + time.sleep(max(0.0, duration)) + if neutral_after: + self.state.move_right_stick(128, 128) + self.send() + def neutral(self) -> None: self.state.neutral() self.send() @@ -319,6 +388,10 @@ class SwitchUARTClient: return None def close(self) -> None: + if self._auto_thread: + self._stop_event.set() + self._auto_thread.join(timeout=0.5) + self._auto_thread = None self.uart.close() def __enter__(self) -> "SwitchUARTClient": From aef37123fdae91efef5f121f1ec5c69e8d97e456 Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Mon, 1 Dec 2025 13:29:04 -0700 Subject: [PATCH 075/134] Refactor --- controller_db/gamecontrollerdb.txt | 2 + controller_uart_bridge.py | 74 +++--------------------------- example_switch_macro.py | 4 +- switch_pico_uart.py | 73 ++++++++++++++++++++++++++++- 4 files changed, 81 insertions(+), 72 deletions(-) diff --git a/controller_db/gamecontrollerdb.txt b/controller_db/gamecontrollerdb.txt index a288a13..20472c2 100644 --- a/controller_db/gamecontrollerdb.txt +++ b/controller_db/gamecontrollerdb.txt @@ -992,6 +992,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 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, 030000006d0400001fc2000000000000,Logitech F710,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,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, +030000006d04000019c2000000020000,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:Mac OS X, 03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a: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, 03000000380700005082000000010000,Mad Catz PS4 Fightpad 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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, @@ -1153,6 +1154,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e040000130b000017050000,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, 030000005e040000130b000022050000,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, 030000005e040000220b000017050000,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, +030000005e040000220b000021050000,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, 03000000172700004431000029010000,XiaoMi 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,a: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,a: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, diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 3b4c50e..4316399 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -26,9 +26,6 @@ from pathlib import Path from typing import Dict, List, Optional, Tuple from serial import SerialException -from serial.tools import list_ports -from serial.tools import list_ports_common - import sdl2 import sdl2.ext from rich.console import Console @@ -44,13 +41,14 @@ from switch_pico_uart import ( axis_to_stick, str_to_dpad, decode_rumble, + discover_serial_ports, trigger_to_button, ) RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this -RUMBLE_MIN_ACTIVE = 0.50 # below this, rumble is treated as off/noise -RUMBLE_SCALE = 0.8 +RUMBLE_MIN_ACTIVE = 0.40 # below this, rumble is treated as off/noise +RUMBLE_SCALE = 1.0 CONTROLLER_DB_URL_DEFAULT = ( "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt" ) @@ -145,66 +143,6 @@ STICK_AXIS_LABELS = ( STICK_AXES = tuple(axis for axis, _ in STICK_AXIS_LABELS) -def is_usb_serial(path: str) -> bool: - """ - Heuristic for USB serial path prefixes (best-effort when VID/PID are missing). - - Accepts common USB-adapter patterns; rejects generic /dev/tty* unless they - clearly indicate USB. - """ - lower = path.lower() - usb_prefixes = ( - "/dev/ttyusb", # Linux USB serial - "/dev/ttyacm", # Linux CDC ACM - "/dev/cu.usb", # macOS cu/tty USB adapters - "/dev/tty.usb", - ) - if lower.startswith(usb_prefixes): - return True - # Default to False for unknown paths; caller can include_non_usb to override. - return False - - -def is_usb_serial_port(port: list_ports_common.ListPortInfo) -> bool: - """Heuristic: prefer ports with USB VID/PID; fall back to path hints.""" - if getattr(port, "vid", None) is not None or getattr(port, "pid", None) is not None: - return True - path = port.device or "" - manufacturer = (getattr(port, "manufacturer", "") or "").upper() - if "USB" in manufacturer: - return True - return is_usb_serial(path) - - -def discover_ports( - include_non_usb: bool = False, - ignore_descriptions: Optional[List[str]] = None, - include_descriptions: Optional[List[str]] = None, -) -> List[Dict[str, str]]: - """List serial ports, optionally filtering by description and USB-ness.""" - ignored = [d.lower() for d in ignore_descriptions or []] - includes = [d.lower() for d in include_descriptions or []] - results: List[Dict[str, str]] = [] - for port in list_ports.comports(): - path = port.device or "" - if not path: - continue - if not include_non_usb and not is_usb_serial_port(port): - continue - desc_lower = (port.description or "").lower() - if includes and not any(keep in desc_lower for keep in includes): - continue - if any(skip in desc_lower for skip in ignored): - continue - results.append( - { - "device": path, - "description": port.description or "Unknown", - } - ) - return results - - def interactive_pairing( console: Console, controller_info: Dict[int, str], ports: List[Dict[str, str]] ) -> List[Tuple[int, str]]: @@ -867,7 +805,7 @@ def prepare_pairing_state( if not controller_indices: parser.error("No controllers detected for interactive pairing.") # Interactive pairing shows the discovered ports and lets the user bind explicitly. - discovered = discover_ports( + discovered = discover_serial_ports( include_non_usb=include_non_usb, ignore_descriptions=ignore_port_desc, include_descriptions=include_port_desc, @@ -883,7 +821,7 @@ def prepare_pairing_state( console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") else: # Passive mode: grab whatever UARTs exist now, and keep looking later. - discovered = discover_ports( + discovered = discover_serial_ports( include_non_usb=include_non_usb, ignore_descriptions=ignore_port_desc, include_descriptions=include_port_desc, @@ -960,7 +898,7 @@ def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerCont """Scan for new serial ports and add unused ones to the available pool.""" if not pairing.auto_discover_ports: return - discovered = discover_ports( + discovered = discover_serial_ports( include_non_usb=pairing.include_non_usb, ignore_descriptions=pairing.ignore_port_desc, include_descriptions=pairing.include_port_desc, diff --git a/example_switch_macro.py b/example_switch_macro.py index deadba7..703e2e9 100644 --- a/example_switch_macro.py +++ b/example_switch_macro.py @@ -1,8 +1,8 @@ # example_switch_macro.py import time -from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchDpad +from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchDpad, first_serial_port -PORT = "COM5" # change to your serial port, e.g. /dev/cu.usbserial-0001 +PORT = first_serial_port(include_descriptions=["USB to UART"]) or "COM5" # auto-pick first serial port, or fall back SEND_INTERVAL = 1 / 500 # optional: match controller_uart_bridge default # Convenience list of every SwitchButton (plus DPAD directions). diff --git a/switch_pico_uart.py b/switch_pico_uart.py index 00ccb98..15bfb5e 100644 --- a/switch_pico_uart.py +++ b/switch_pico_uart.py @@ -17,9 +17,10 @@ import time import threading from dataclasses import dataclass, field from enum import IntEnum, IntFlag -from typing import Iterable, Mapping, Optional, Tuple, Union +from typing import Iterable, Mapping, Optional, Tuple, Union, List, Dict import serial +from serial.tools import list_ports, list_ports_common UART_HEADER = 0xAA RUMBLE_HEADER = 0xBB @@ -57,6 +58,74 @@ class SwitchDpad(IntEnum): CENTER = 0x08 +def _is_usb_serial_path(path: str) -> bool: + """Heuristic for USB serial path prefixes.""" + lower = path.lower() + usb_prefixes = ( + "/dev/ttyusb", # Linux USB serial + "/dev/ttyacm", # Linux CDC ACM + "/dev/cu.usb", # macOS cu/tty USB adapters + "/dev/tty.usb", + ) + if lower.startswith(usb_prefixes): + return True + # Windows COM ports don't clearly indicate USB; treat as unknown here. + return False + + +def _is_usb_serial_port(port: list_ports_common.ListPortInfo) -> bool: + """Heuristic: prefer ports with USB VID/PID; fall back to path hints.""" + if getattr(port, "vid", None) is not None or getattr(port, "pid", None) is not None: + return True + path = port.device or "" + manufacturer = (getattr(port, "manufacturer", "") or "").upper() + if "USB" in manufacturer: + return True + return _is_usb_serial_path(path) + + +def discover_serial_ports( + include_non_usb: bool = False, + ignore_descriptions: Optional[List[str]] = None, + include_descriptions: Optional[List[str]] = None, +) -> List[Dict[str, str]]: + """ + List serial ports with simple filtering similar to controller_uart_bridge. + + Args: + include_non_usb: Include ports that don't look USB-based (e.g., onboard UARTs). + ignore_descriptions: Substrings (case-insensitive) to exclude by description. + include_descriptions: If provided, only include ports whose description contains one of these substrings. + """ + ignored = [d.lower() for d in (ignore_descriptions or [])] + includes = [d.lower() for d in (include_descriptions or [])] + results: List[Dict[str, str]] = [] + for port in list_ports.comports(): + path = port.device or "" + if not path: + continue + if not include_non_usb and not _is_usb_serial_port(port): + continue + desc_lower = (port.description or "").lower() + if includes and not any(keep in desc_lower for keep in includes): + continue + if any(skip in desc_lower for skip in ignored): + continue + results.append({"device": path, "description": port.description or "Unknown"}) + return results + + +def first_serial_port( + include_non_usb: bool = False, + ignore_descriptions: Optional[List[str]] = None, + include_descriptions: Optional[List[str]] = None, +) -> Optional[str]: + """Return the first discovered serial port path (or None if none are found).""" + ports = discover_serial_ports(include_non_usb, ignore_descriptions, include_descriptions) + if not ports: + return None + return ports[0]["device"] + def clamp_byte(value: Union[int, float]) -> int: """Clamp a numeric value to the 0-255 byte range.""" return max(0, min(255, int(value))) @@ -90,7 +159,7 @@ def trigger_to_button(value: int, threshold: int) -> bool: def str_to_dpad(flags: Mapping[str, bool]) -> SwitchDpad: - """Translate DPAD button flags into a Switch hat value.""" + """Translate DPAD button flags into a Switch hat/DPAD value.""" up = flags.get("up", False) down = flags.get("down", False) left = flags.get("left", False) From cd26ddb865f7af0843b170f15d27315f3d9d5be5 Mon Sep 17 00:00:00 2001 From: jojomawswan Date: Mon, 1 Dec 2025 13:29:04 -0700 Subject: [PATCH 076/134] Refactor --- controller_db/gamecontrollerdb.txt | 2 + controller_uart_bridge.py | 74 +++--------------------------- example_switch_macro.py | 4 +- switch_pico_uart.py | 73 ++++++++++++++++++++++++++++- 4 files changed, 81 insertions(+), 72 deletions(-) diff --git a/controller_db/gamecontrollerdb.txt b/controller_db/gamecontrollerdb.txt index a288a13..20472c2 100644 --- a/controller_db/gamecontrollerdb.txt +++ b/controller_db/gamecontrollerdb.txt @@ -992,6 +992,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 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, 030000006d0400001fc2000000000000,Logitech F710,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,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, +030000006d04000019c2000000020000,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:Mac OS X, 03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a: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, 03000000380700005082000000010000,Mad Catz PS4 Fightpad 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,touchpad:b13,x:b0,y:b3,platform:Mac OS X, @@ -1153,6 +1154,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e040000130b000017050000,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, 030000005e040000130b000022050000,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, 030000005e040000220b000017050000,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, +030000005e040000220b000021050000,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, 03000000172700004431000029010000,XiaoMi 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,a: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,a: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, diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 3b4c50e..4316399 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -26,9 +26,6 @@ from pathlib import Path from typing import Dict, List, Optional, Tuple from serial import SerialException -from serial.tools import list_ports -from serial.tools import list_ports_common - import sdl2 import sdl2.ext from rich.console import Console @@ -44,13 +41,14 @@ from switch_pico_uart import ( axis_to_stick, str_to_dpad, decode_rumble, + discover_serial_ports, trigger_to_button, ) RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this -RUMBLE_MIN_ACTIVE = 0.50 # below this, rumble is treated as off/noise -RUMBLE_SCALE = 0.8 +RUMBLE_MIN_ACTIVE = 0.40 # below this, rumble is treated as off/noise +RUMBLE_SCALE = 1.0 CONTROLLER_DB_URL_DEFAULT = ( "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt" ) @@ -145,66 +143,6 @@ STICK_AXIS_LABELS = ( STICK_AXES = tuple(axis for axis, _ in STICK_AXIS_LABELS) -def is_usb_serial(path: str) -> bool: - """ - Heuristic for USB serial path prefixes (best-effort when VID/PID are missing). - - Accepts common USB-adapter patterns; rejects generic /dev/tty* unless they - clearly indicate USB. - """ - lower = path.lower() - usb_prefixes = ( - "/dev/ttyusb", # Linux USB serial - "/dev/ttyacm", # Linux CDC ACM - "/dev/cu.usb", # macOS cu/tty USB adapters - "/dev/tty.usb", - ) - if lower.startswith(usb_prefixes): - return True - # Default to False for unknown paths; caller can include_non_usb to override. - return False - - -def is_usb_serial_port(port: list_ports_common.ListPortInfo) -> bool: - """Heuristic: prefer ports with USB VID/PID; fall back to path hints.""" - if getattr(port, "vid", None) is not None or getattr(port, "pid", None) is not None: - return True - path = port.device or "" - manufacturer = (getattr(port, "manufacturer", "") or "").upper() - if "USB" in manufacturer: - return True - return is_usb_serial(path) - - -def discover_ports( - include_non_usb: bool = False, - ignore_descriptions: Optional[List[str]] = None, - include_descriptions: Optional[List[str]] = None, -) -> List[Dict[str, str]]: - """List serial ports, optionally filtering by description and USB-ness.""" - ignored = [d.lower() for d in ignore_descriptions or []] - includes = [d.lower() for d in include_descriptions or []] - results: List[Dict[str, str]] = [] - for port in list_ports.comports(): - path = port.device or "" - if not path: - continue - if not include_non_usb and not is_usb_serial_port(port): - continue - desc_lower = (port.description or "").lower() - if includes and not any(keep in desc_lower for keep in includes): - continue - if any(skip in desc_lower for skip in ignored): - continue - results.append( - { - "device": path, - "description": port.description or "Unknown", - } - ) - return results - - def interactive_pairing( console: Console, controller_info: Dict[int, str], ports: List[Dict[str, str]] ) -> List[Tuple[int, str]]: @@ -867,7 +805,7 @@ def prepare_pairing_state( if not controller_indices: parser.error("No controllers detected for interactive pairing.") # Interactive pairing shows the discovered ports and lets the user bind explicitly. - discovered = discover_ports( + discovered = discover_serial_ports( include_non_usb=include_non_usb, ignore_descriptions=ignore_port_desc, include_descriptions=include_port_desc, @@ -883,7 +821,7 @@ def prepare_pairing_state( console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") else: # Passive mode: grab whatever UARTs exist now, and keep looking later. - discovered = discover_ports( + discovered = discover_serial_ports( include_non_usb=include_non_usb, ignore_descriptions=ignore_port_desc, include_descriptions=include_port_desc, @@ -960,7 +898,7 @@ def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerCont """Scan for new serial ports and add unused ones to the available pool.""" if not pairing.auto_discover_ports: return - discovered = discover_ports( + discovered = discover_serial_ports( include_non_usb=pairing.include_non_usb, ignore_descriptions=pairing.ignore_port_desc, include_descriptions=pairing.include_port_desc, diff --git a/example_switch_macro.py b/example_switch_macro.py index deadba7..703e2e9 100644 --- a/example_switch_macro.py +++ b/example_switch_macro.py @@ -1,8 +1,8 @@ # example_switch_macro.py import time -from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchDpad +from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchDpad, first_serial_port -PORT = "COM5" # change to your serial port, e.g. /dev/cu.usbserial-0001 +PORT = first_serial_port(include_descriptions=["USB to UART"]) or "COM5" # auto-pick first serial port, or fall back SEND_INTERVAL = 1 / 500 # optional: match controller_uart_bridge default # Convenience list of every SwitchButton (plus DPAD directions). diff --git a/switch_pico_uart.py b/switch_pico_uart.py index 00ccb98..15bfb5e 100644 --- a/switch_pico_uart.py +++ b/switch_pico_uart.py @@ -17,9 +17,10 @@ import time import threading from dataclasses import dataclass, field from enum import IntEnum, IntFlag -from typing import Iterable, Mapping, Optional, Tuple, Union +from typing import Iterable, Mapping, Optional, Tuple, Union, List, Dict import serial +from serial.tools import list_ports, list_ports_common UART_HEADER = 0xAA RUMBLE_HEADER = 0xBB @@ -57,6 +58,74 @@ class SwitchDpad(IntEnum): CENTER = 0x08 +def _is_usb_serial_path(path: str) -> bool: + """Heuristic for USB serial path prefixes.""" + lower = path.lower() + usb_prefixes = ( + "/dev/ttyusb", # Linux USB serial + "/dev/ttyacm", # Linux CDC ACM + "/dev/cu.usb", # macOS cu/tty USB adapters + "/dev/tty.usb", + ) + if lower.startswith(usb_prefixes): + return True + # Windows COM ports don't clearly indicate USB; treat as unknown here. + return False + + +def _is_usb_serial_port(port: list_ports_common.ListPortInfo) -> bool: + """Heuristic: prefer ports with USB VID/PID; fall back to path hints.""" + if getattr(port, "vid", None) is not None or getattr(port, "pid", None) is not None: + return True + path = port.device or "" + manufacturer = (getattr(port, "manufacturer", "") or "").upper() + if "USB" in manufacturer: + return True + return _is_usb_serial_path(path) + + +def discover_serial_ports( + include_non_usb: bool = False, + ignore_descriptions: Optional[List[str]] = None, + include_descriptions: Optional[List[str]] = None, +) -> List[Dict[str, str]]: + """ + List serial ports with simple filtering similar to controller_uart_bridge. + + Args: + include_non_usb: Include ports that don't look USB-based (e.g., onboard UARTs). + ignore_descriptions: Substrings (case-insensitive) to exclude by description. + include_descriptions: If provided, only include ports whose description contains one of these substrings. + """ + ignored = [d.lower() for d in (ignore_descriptions or [])] + includes = [d.lower() for d in (include_descriptions or [])] + results: List[Dict[str, str]] = [] + for port in list_ports.comports(): + path = port.device or "" + if not path: + continue + if not include_non_usb and not _is_usb_serial_port(port): + continue + desc_lower = (port.description or "").lower() + if includes and not any(keep in desc_lower for keep in includes): + continue + if any(skip in desc_lower for skip in ignored): + continue + results.append({"device": path, "description": port.description or "Unknown"}) + return results + + +def first_serial_port( + include_non_usb: bool = False, + ignore_descriptions: Optional[List[str]] = None, + include_descriptions: Optional[List[str]] = None, +) -> Optional[str]: + """Return the first discovered serial port path (or None if none are found).""" + ports = discover_serial_ports(include_non_usb, ignore_descriptions, include_descriptions) + if not ports: + return None + return ports[0]["device"] + def clamp_byte(value: Union[int, float]) -> int: """Clamp a numeric value to the 0-255 byte range.""" return max(0, min(255, int(value))) @@ -90,7 +159,7 @@ def trigger_to_button(value: int, threshold: int) -> bool: def str_to_dpad(flags: Mapping[str, bool]) -> SwitchDpad: - """Translate DPAD button flags into a Switch hat value.""" + """Translate DPAD button flags into a Switch hat/DPAD value.""" up = flags.get("up", False) down = flags.get("down", False) left = flags.get("left", False) From 5779599000d2ab2c39425d632a40c9a732b164e4 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 6 Dec 2025 11:41:53 -0700 Subject: [PATCH 077/134] Add build.py --- build.py | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 build.py diff --git a/build.py b/build.py new file mode 100644 index 0000000..67a3500 --- /dev/null +++ b/build.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +"""Build and flash the project with optional grip color overrides.""" +import argparse +import os +import random +import re +import shutil +import subprocess +import sys +from pathlib import Path + +SCRIPT_DIR = Path(__file__).resolve().parent +CONFIG_FILE = SCRIPT_DIR / "controller_color_config.h" +BUILD_DIR = SCRIPT_DIR / "build" + +ELF_PATH = Path(os.environ.get("ELF_PATH", BUILD_DIR / "switch-pico.elf")).expanduser() + +MACROS = ( + "SWITCH_COLOR_LEFT_GRIP_R", + "SWITCH_COLOR_LEFT_GRIP_G", + "SWITCH_COLOR_LEFT_GRIP_B", + "SWITCH_COLOR_RIGHT_GRIP_R", + "SWITCH_COLOR_RIGHT_GRIP_G", + "SWITCH_COLOR_RIGHT_GRIP_B", +) + +def parse_args(): + parser = argparse.ArgumentParser( + description="Build and flash the project, optionally setting grip colors.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog="Default behavior leaves controller_color_config.h unchanged.", + ) + group = parser.add_mutually_exclusive_group() + group.add_argument( + "--random-grip-color", + action="store_true", + help="Randomize both grip colors before building.", + ) + group.add_argument( + "--grip-color", + metavar="RRGGBB", + help="Set both grip colors to the provided hex value.", + ) + return parser.parse_args() + +def random_hex_color(): + return "".join(f"{random.randrange(256):02X}" for _ in range(3)) + +def validate_custom_color(value): + if not re.fullmatch(r"[0-9A-Fa-f]{6}", value): + raise ValueError("Color must be a 6-digit hex value like FF8800.") + return value + +def update_grip_colors(rgb_hex): + if not CONFIG_FILE.exists(): + sys.stderr.write(f"Error: Cannot find {CONFIG_FILE}\n") + sys.exit(1) + + r, g, b = rgb_hex[:2], rgb_hex[2:4], rgb_hex[4:6] + + try: + text = CONFIG_FILE.read_text(encoding="utf-8") + except OSError as exc: + sys.stderr.write(f"Error reading {CONFIG_FILE}: {exc}\n") + sys.exit(1) + + def replace(name, val, data): + pattern = rf"(?m)^(#define\s+{name}\s+)0x[0-9A-Fa-f]{{2}}" + updated, count = re.subn(pattern, rf"\g<1>0x{val.upper()}", data) + if count == 0: + sys.stderr.write(f"Error: Could not find {name} in {CONFIG_FILE}\n") + sys.exit(1) + return updated + + values = (r, g, b, r, g, b) + for macro, val in zip(MACROS, values): + text = replace(macro, val, text) + + try: + CONFIG_FILE.write_text(text, encoding="utf-8") + except OSError as exc: + sys.stderr.write(f"Error writing {CONFIG_FILE}: {exc}\n") + sys.exit(1) + +def run_cmd(command): + try: + subprocess.run(command, cwd=SCRIPT_DIR, check=True) + except FileNotFoundError as exc: + sys.stderr.write(f"Error running {command[0]}: {exc}\n") + sys.exit(1) + except subprocess.CalledProcessError as exc: + sys.exit(exc.returncode) + +def resolve_picotool(): + env_val = os.environ.get("PICOTOOL_PATH") + if env_val: + env_path = Path(env_val).expanduser() + if not env_path.exists(): + sys.stderr.write(f"Error: PICOTOOL_PATH set to {env_path}, but it does not exist.\n") + sys.exit(1) + return env_path + + found = shutil.which("picotool") + if found: + return Path(found) + + sys.stderr.write("Error: picotool not found. Put it on your PATH or set PICOTOOL_PATH.\n") + sys.exit(1) + +def build(): + run_cmd( + [ + "cmake", + "-S", + str(SCRIPT_DIR), + "-B", + str(BUILD_DIR), + "-DSWITCH_PICO_AUTOTEST=OFF", + "-DSWITCH_PICO_LOG=OFF", + ] + ) + run_cmd(["cmake", "--build", str(BUILD_DIR)]) + +def flash(): + picotool = resolve_picotool() + if not ELF_PATH.exists(): + sys.stderr.write( + f"Error: Cannot find ELF at {ELF_PATH}. Set ELF_PATH to override.\n" + ) + sys.exit(1) + run_cmd([str(picotool), "load", str(ELF_PATH), "-fx"]) + +def main(): + args = parse_args() + color = None + + if args.random_grip_color: + color = random_hex_color() + elif args.grip_color: + try: + color = validate_custom_color(args.grip_color) + except ValueError as exc: + sys.stderr.write(f"Error: {exc}\n") + sys.exit(1) + + if color: + update_grip_colors(color) + print(f"Grip color set to #{color} in {CONFIG_FILE.name}") + + build() + flash() + +if __name__ == "__main__": + main() From b09e4b3397184320a9d5b233acd9739fdd18a320 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 6 Dec 2025 11:41:53 -0700 Subject: [PATCH 078/134] Add build.py --- build.py | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 build.py diff --git a/build.py b/build.py new file mode 100644 index 0000000..67a3500 --- /dev/null +++ b/build.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +"""Build and flash the project with optional grip color overrides.""" +import argparse +import os +import random +import re +import shutil +import subprocess +import sys +from pathlib import Path + +SCRIPT_DIR = Path(__file__).resolve().parent +CONFIG_FILE = SCRIPT_DIR / "controller_color_config.h" +BUILD_DIR = SCRIPT_DIR / "build" + +ELF_PATH = Path(os.environ.get("ELF_PATH", BUILD_DIR / "switch-pico.elf")).expanduser() + +MACROS = ( + "SWITCH_COLOR_LEFT_GRIP_R", + "SWITCH_COLOR_LEFT_GRIP_G", + "SWITCH_COLOR_LEFT_GRIP_B", + "SWITCH_COLOR_RIGHT_GRIP_R", + "SWITCH_COLOR_RIGHT_GRIP_G", + "SWITCH_COLOR_RIGHT_GRIP_B", +) + +def parse_args(): + parser = argparse.ArgumentParser( + description="Build and flash the project, optionally setting grip colors.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog="Default behavior leaves controller_color_config.h unchanged.", + ) + group = parser.add_mutually_exclusive_group() + group.add_argument( + "--random-grip-color", + action="store_true", + help="Randomize both grip colors before building.", + ) + group.add_argument( + "--grip-color", + metavar="RRGGBB", + help="Set both grip colors to the provided hex value.", + ) + return parser.parse_args() + +def random_hex_color(): + return "".join(f"{random.randrange(256):02X}" for _ in range(3)) + +def validate_custom_color(value): + if not re.fullmatch(r"[0-9A-Fa-f]{6}", value): + raise ValueError("Color must be a 6-digit hex value like FF8800.") + return value + +def update_grip_colors(rgb_hex): + if not CONFIG_FILE.exists(): + sys.stderr.write(f"Error: Cannot find {CONFIG_FILE}\n") + sys.exit(1) + + r, g, b = rgb_hex[:2], rgb_hex[2:4], rgb_hex[4:6] + + try: + text = CONFIG_FILE.read_text(encoding="utf-8") + except OSError as exc: + sys.stderr.write(f"Error reading {CONFIG_FILE}: {exc}\n") + sys.exit(1) + + def replace(name, val, data): + pattern = rf"(?m)^(#define\s+{name}\s+)0x[0-9A-Fa-f]{{2}}" + updated, count = re.subn(pattern, rf"\g<1>0x{val.upper()}", data) + if count == 0: + sys.stderr.write(f"Error: Could not find {name} in {CONFIG_FILE}\n") + sys.exit(1) + return updated + + values = (r, g, b, r, g, b) + for macro, val in zip(MACROS, values): + text = replace(macro, val, text) + + try: + CONFIG_FILE.write_text(text, encoding="utf-8") + except OSError as exc: + sys.stderr.write(f"Error writing {CONFIG_FILE}: {exc}\n") + sys.exit(1) + +def run_cmd(command): + try: + subprocess.run(command, cwd=SCRIPT_DIR, check=True) + except FileNotFoundError as exc: + sys.stderr.write(f"Error running {command[0]}: {exc}\n") + sys.exit(1) + except subprocess.CalledProcessError as exc: + sys.exit(exc.returncode) + +def resolve_picotool(): + env_val = os.environ.get("PICOTOOL_PATH") + if env_val: + env_path = Path(env_val).expanduser() + if not env_path.exists(): + sys.stderr.write(f"Error: PICOTOOL_PATH set to {env_path}, but it does not exist.\n") + sys.exit(1) + return env_path + + found = shutil.which("picotool") + if found: + return Path(found) + + sys.stderr.write("Error: picotool not found. Put it on your PATH or set PICOTOOL_PATH.\n") + sys.exit(1) + +def build(): + run_cmd( + [ + "cmake", + "-S", + str(SCRIPT_DIR), + "-B", + str(BUILD_DIR), + "-DSWITCH_PICO_AUTOTEST=OFF", + "-DSWITCH_PICO_LOG=OFF", + ] + ) + run_cmd(["cmake", "--build", str(BUILD_DIR)]) + +def flash(): + picotool = resolve_picotool() + if not ELF_PATH.exists(): + sys.stderr.write( + f"Error: Cannot find ELF at {ELF_PATH}. Set ELF_PATH to override.\n" + ) + sys.exit(1) + run_cmd([str(picotool), "load", str(ELF_PATH), "-fx"]) + +def main(): + args = parse_args() + color = None + + if args.random_grip_color: + color = random_hex_color() + elif args.grip_color: + try: + color = validate_custom_color(args.grip_color) + except ValueError as exc: + sys.stderr.write(f"Error: {exc}\n") + sys.exit(1) + + if color: + update_grip_colors(color) + print(f"Grip color set to #{color} in {CONFIG_FILE.name}") + + build() + flash() + +if __name__ == "__main__": + main() From 6a8bf6c27f40246eb67b54bb2a02794f6b5f6723 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 6 Dec 2025 11:53:36 -0700 Subject: [PATCH 079/134] Add manufacturer --- controller_uart_bridge.py | 12 ++++++++++++ switch_pico_uart.py | 22 ++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 4316399..ebd2330 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -583,6 +583,12 @@ def build_arg_parser() -> argparse.ArgumentParser: default=[], help="Only include serial ports whose description contains this substring (case-insensitive). Repeatable.", ) + parser.add_argument( + "--include-port-manufacturer", + action="append", + default=[], + help="Only include serial ports whose manufacturer contains this substring (case-insensitive). Repeatable.", + ) parser.add_argument( "--include-controller-name", action="append", @@ -673,6 +679,7 @@ class PairingState: include_non_usb: bool = False ignore_port_desc: List[str] = field(default_factory=list) include_port_desc: List[str] = field(default_factory=list) + include_port_mfr: List[str] = field(default_factory=list) def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, SwitchButton], Dict[int, SwitchButton], set[int]]: @@ -798,6 +805,7 @@ def prepare_pairing_state( include_non_usb = args.all_ports or False ignore_port_desc = [d.lower() for d in args.ignore_port_desc] include_port_desc = [d.lower() for d in args.include_port_desc] + include_port_mfr = [m.lower() for m in args.include_port_manufacturer] available_ports: List[str] = [] mappings = list(args.map) @@ -809,6 +817,7 @@ def prepare_pairing_state( include_non_usb=include_non_usb, ignore_descriptions=ignore_port_desc, include_descriptions=include_port_desc, + include_manufacturers=include_port_mfr, ) if not discovered: parser.error("No UART devices found for interactive pairing.") @@ -825,6 +834,7 @@ def prepare_pairing_state( include_non_usb=include_non_usb, ignore_descriptions=ignore_port_desc, include_descriptions=include_port_desc, + include_manufacturers=include_port_mfr, ) if discovered: available_ports.extend(info["device"] for info in discovered) @@ -843,6 +853,7 @@ def prepare_pairing_state( include_non_usb=include_non_usb, ignore_port_desc=ignore_port_desc, include_port_desc=include_port_desc, + include_port_mfr=include_port_mfr, ) @@ -902,6 +913,7 @@ def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerCont include_non_usb=pairing.include_non_usb, ignore_descriptions=pairing.ignore_port_desc, include_descriptions=pairing.include_port_desc, + include_manufacturers=pairing.include_port_mfr, ) current_paths = {info["device"] for info in discovered} known_paths = set(pairing.available_ports) diff --git a/switch_pico_uart.py b/switch_pico_uart.py index 15bfb5e..eb4fa25 100644 --- a/switch_pico_uart.py +++ b/switch_pico_uart.py @@ -88,6 +88,7 @@ def discover_serial_ports( include_non_usb: bool = False, ignore_descriptions: Optional[List[str]] = None, include_descriptions: Optional[List[str]] = None, + include_manufacturers: Optional[List[str]] = None, ) -> List[Dict[str, str]]: """ List serial ports with simple filtering similar to controller_uart_bridge. @@ -96,9 +97,11 @@ def discover_serial_ports( include_non_usb: Include ports that don't look USB-based (e.g., onboard UARTs). ignore_descriptions: Substrings (case-insensitive) to exclude by description. include_descriptions: If provided, only include ports whose description contains one of these substrings. + include_manufacturers: If provided, only include ports whose manufacturer contains one of these substrings. """ ignored = [d.lower() for d in (ignore_descriptions or [])] includes = [d.lower() for d in (include_descriptions or [])] + include_mfrs = [m.lower() for m in (include_manufacturers or [])] results: List[Dict[str, str]] = [] for port in list_ports.comports(): path = port.device or "" @@ -107,11 +110,20 @@ def discover_serial_ports( if not include_non_usb and not _is_usb_serial_port(port): continue desc_lower = (port.description or "").lower() + mfr_lower = (port.manufacturer or "").lower() + if include_mfrs and not any(keep in mfr_lower for keep in include_mfrs): + continue if includes and not any(keep in desc_lower for keep in includes): continue if any(skip in desc_lower for skip in ignored): continue - results.append({"device": path, "description": port.description or "Unknown"}) + results.append( + { + "device": path, + "description": port.description or "Unknown", + "manufacturer": port.manufacturer or "", + } + ) return results @@ -119,9 +131,15 @@ def first_serial_port( include_non_usb: bool = False, ignore_descriptions: Optional[List[str]] = None, include_descriptions: Optional[List[str]] = None, + include_manufacturers: Optional[List[str]] = None, ) -> Optional[str]: """Return the first discovered serial port path (or None if none are found).""" - ports = discover_serial_ports(include_non_usb, ignore_descriptions, include_descriptions) + ports = discover_serial_ports( + include_non_usb, + ignore_descriptions, + include_descriptions, + include_manufacturers, + ) if not ports: return None return ports[0]["device"] From 10dfbd3a0820f9dacf3215592346114d031b421f Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 6 Dec 2025 11:53:36 -0700 Subject: [PATCH 080/134] Add manufacturer --- controller_uart_bridge.py | 12 ++++++++++++ switch_pico_uart.py | 22 ++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/controller_uart_bridge.py b/controller_uart_bridge.py index 4316399..ebd2330 100644 --- a/controller_uart_bridge.py +++ b/controller_uart_bridge.py @@ -583,6 +583,12 @@ def build_arg_parser() -> argparse.ArgumentParser: default=[], help="Only include serial ports whose description contains this substring (case-insensitive). Repeatable.", ) + parser.add_argument( + "--include-port-manufacturer", + action="append", + default=[], + help="Only include serial ports whose manufacturer contains this substring (case-insensitive). Repeatable.", + ) parser.add_argument( "--include-controller-name", action="append", @@ -673,6 +679,7 @@ class PairingState: include_non_usb: bool = False ignore_port_desc: List[str] = field(default_factory=list) include_port_desc: List[str] = field(default_factory=list) + include_port_mfr: List[str] = field(default_factory=list) def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, SwitchButton], Dict[int, SwitchButton], set[int]]: @@ -798,6 +805,7 @@ def prepare_pairing_state( include_non_usb = args.all_ports or False ignore_port_desc = [d.lower() for d in args.ignore_port_desc] include_port_desc = [d.lower() for d in args.include_port_desc] + include_port_mfr = [m.lower() for m in args.include_port_manufacturer] available_ports: List[str] = [] mappings = list(args.map) @@ -809,6 +817,7 @@ def prepare_pairing_state( include_non_usb=include_non_usb, ignore_descriptions=ignore_port_desc, include_descriptions=include_port_desc, + include_manufacturers=include_port_mfr, ) if not discovered: parser.error("No UART devices found for interactive pairing.") @@ -825,6 +834,7 @@ def prepare_pairing_state( include_non_usb=include_non_usb, ignore_descriptions=ignore_port_desc, include_descriptions=include_port_desc, + include_manufacturers=include_port_mfr, ) if discovered: available_ports.extend(info["device"] for info in discovered) @@ -843,6 +853,7 @@ def prepare_pairing_state( include_non_usb=include_non_usb, ignore_port_desc=ignore_port_desc, include_port_desc=include_port_desc, + include_port_mfr=include_port_mfr, ) @@ -902,6 +913,7 @@ def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerCont include_non_usb=pairing.include_non_usb, ignore_descriptions=pairing.ignore_port_desc, include_descriptions=pairing.include_port_desc, + include_manufacturers=pairing.include_port_mfr, ) current_paths = {info["device"] for info in discovered} known_paths = set(pairing.available_ports) diff --git a/switch_pico_uart.py b/switch_pico_uart.py index 15bfb5e..eb4fa25 100644 --- a/switch_pico_uart.py +++ b/switch_pico_uart.py @@ -88,6 +88,7 @@ def discover_serial_ports( include_non_usb: bool = False, ignore_descriptions: Optional[List[str]] = None, include_descriptions: Optional[List[str]] = None, + include_manufacturers: Optional[List[str]] = None, ) -> List[Dict[str, str]]: """ List serial ports with simple filtering similar to controller_uart_bridge. @@ -96,9 +97,11 @@ def discover_serial_ports( include_non_usb: Include ports that don't look USB-based (e.g., onboard UARTs). ignore_descriptions: Substrings (case-insensitive) to exclude by description. include_descriptions: If provided, only include ports whose description contains one of these substrings. + include_manufacturers: If provided, only include ports whose manufacturer contains one of these substrings. """ ignored = [d.lower() for d in (ignore_descriptions or [])] includes = [d.lower() for d in (include_descriptions or [])] + include_mfrs = [m.lower() for m in (include_manufacturers or [])] results: List[Dict[str, str]] = [] for port in list_ports.comports(): path = port.device or "" @@ -107,11 +110,20 @@ def discover_serial_ports( if not include_non_usb and not _is_usb_serial_port(port): continue desc_lower = (port.description or "").lower() + mfr_lower = (port.manufacturer or "").lower() + if include_mfrs and not any(keep in mfr_lower for keep in include_mfrs): + continue if includes and not any(keep in desc_lower for keep in includes): continue if any(skip in desc_lower for skip in ignored): continue - results.append({"device": path, "description": port.description or "Unknown"}) + results.append( + { + "device": path, + "description": port.description or "Unknown", + "manufacturer": port.manufacturer or "", + } + ) return results @@ -119,9 +131,15 @@ def first_serial_port( include_non_usb: bool = False, ignore_descriptions: Optional[List[str]] = None, include_descriptions: Optional[List[str]] = None, + include_manufacturers: Optional[List[str]] = None, ) -> Optional[str]: """Return the first discovered serial port path (or None if none are found).""" - ports = discover_serial_ports(include_non_usb, ignore_descriptions, include_descriptions) + ports = discover_serial_ports( + include_non_usb, + ignore_descriptions, + include_descriptions, + include_manufacturers, + ) if not ports: return None return ports[0]["device"] From ef3b08352c8909342758504106ea28851df04619 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 8 Dec 2025 19:20:24 -0700 Subject: [PATCH 081/134] Remove autotest --- CMakeLists.txt | 4 --- switch-pico.cpp | 80 ++----------------------------------------------- 2 files changed, 2 insertions(+), 82 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 896fe25..10a816f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,6 @@ if (EXISTS ${picoVscode}) include(${picoVscode}) endif() # ==================================================================================== -option(SWITCH_PICO_AUTOTEST "Auto-play grip-screen connection sequence on startup" OFF) option(SWITCH_PICO_LOG "Enable UART debug logging" OFF) set(PICO_BOARD pico CACHE STRING "Board type") @@ -59,9 +58,6 @@ target_link_libraries(switch-pico pico_rand ) -if (SWITCH_PICO_AUTOTEST) - target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_AUTOTEST=1) -endif() if (SWITCH_PICO_LOG) target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_LOG=1) endif() diff --git a/switch-pico.cpp b/switch-pico.cpp index 646bbe0..5efd6e9 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -26,14 +26,6 @@ static bool g_last_ready = false; // Track the latest state provided by UART or the autopilot. static SwitchInputState g_user_state; -#ifdef SWITCH_PICO_AUTOTEST -static bool g_autopilot_active = true; -static uint32_t g_autopilot_counter = 0; -static absolute_time_t g_autopilot_last_tick = {0}; -static bool g_uart_activity = false; -static bool g_ready_logged = false; -#endif - static void init_uart_input() { uart_init(UART_ID, BAUD_RATE); gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); @@ -117,65 +109,11 @@ static void poll_uart_frames() { parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING, parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8); } -#ifdef SWITCH_PICO_AUTOTEST - g_uart_activity = true; -#endif index = 0; } } } -#ifdef SWITCH_PICO_AUTOTEST -// Replays the Switch-Fightstick grip-screen sequence: press L+R twice, then A twice. -static SwitchInputState autopilot_state(const SwitchInputState& fallback) { - if (!g_autopilot_active || g_uart_activity) { - g_autopilot_active = false; - return fallback; - } - - if (!tud_mounted()) { - g_autopilot_counter = 0; - g_autopilot_last_tick = {0}; - return fallback; - } - - absolute_time_t now = get_absolute_time(); - if (!to_us_since_boot(g_autopilot_last_tick)) { - g_autopilot_last_tick = now; - } - - // Run at ~1ms cadence similar to the LUFA fightstick timing. - if (absolute_time_diff_us(g_autopilot_last_tick, now) < 1000) { - return fallback; - } - g_autopilot_last_tick = now; - - SwitchInputState state = fallback; - state.lx = SWITCH_PRO_JOYSTICK_MID; - state.ly = SWITCH_PRO_JOYSTICK_MID; - state.rx = SWITCH_PRO_JOYSTICK_MID; - state.ry = SWITCH_PRO_JOYSTICK_MID; - - // Fire L+R twice then A twice, loop every ~300ms to keep trying. - // Hold each press for a few ms so it survives the 5ms USB report throttle. - uint32_t step = g_autopilot_counter % 300; - constexpr uint32_t press_width = 50; // ~12ms - auto in_window = [](uint32_t v, uint32_t start, uint32_t width) { - return v >= start && v < start + width; - }; - bool lr_down = in_window(step, 25, press_width) || in_window(step, 50, press_width); - bool a_down = in_window(step, 75, press_width) || in_window(step, 100, press_width); - - state.button_r = lr_down; - state.button_l = lr_down; - state.button_a = a_down; - - g_autopilot_counter++; - - return state; -} -#endif - static void log_usb_state() { bool mounted = tud_mounted(); bool ready = switch_pro_is_ready(); @@ -188,12 +126,6 @@ static void log_usb_state() { g_last_ready = ready; LOG_PRINTF("[SWITCH] driver %s\n", ready ? "ready (handshake OK)" : "not ready"); } -#ifdef SWITCH_PICO_AUTOTEST - if (ready && !g_ready_logged) { - g_ready_logged = true; - LOG_PRINTF("[AUTO] ready -> autopilot active=%s\n", g_autopilot_active ? "true" : "false"); - } -#endif } int main() { @@ -209,21 +141,13 @@ int main() { switch_pro_set_input(g_user_state); LOG_PRINTF("[BOOT] switch-pico starting (UART0 log @ 115200)\n"); - LOG_PRINTF("[INFO] AUTOTEST=%s UART1 pins TX=%d RX=%d baud=%d\n", - #ifdef SWITCH_PICO_AUTOTEST - "ON" - #else - "OFF" - #endif - , UART_TX_PIN, UART_RX_PIN, BAUD_RATE); + LOG_PRINTF("[INFO] UART1 pins TX=%d RX=%d baud=%d\n", + UART_TX_PIN, UART_RX_PIN, BAUD_RATE); while (true) { tud_task(); // USB device tasks poll_uart_frames(); // Pull controller state from UART1 SwitchInputState state = g_user_state; -#ifdef SWITCH_PICO_AUTOTEST - state = autopilot_state(state); -#endif switch_pro_set_input(state); switch_pro_task(); // Push state to the Switch host log_usb_state(); From 0a8134ae155b9755ee6adb0472dbf3fa876c668b Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 8 Dec 2025 19:20:24 -0700 Subject: [PATCH 082/134] Remove autotest --- CMakeLists.txt | 4 --- switch-pico.cpp | 80 ++----------------------------------------------- 2 files changed, 2 insertions(+), 82 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 896fe25..10a816f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,6 @@ if (EXISTS ${picoVscode}) include(${picoVscode}) endif() # ==================================================================================== -option(SWITCH_PICO_AUTOTEST "Auto-play grip-screen connection sequence on startup" OFF) option(SWITCH_PICO_LOG "Enable UART debug logging" OFF) set(PICO_BOARD pico CACHE STRING "Board type") @@ -59,9 +58,6 @@ target_link_libraries(switch-pico pico_rand ) -if (SWITCH_PICO_AUTOTEST) - target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_AUTOTEST=1) -endif() if (SWITCH_PICO_LOG) target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_LOG=1) endif() diff --git a/switch-pico.cpp b/switch-pico.cpp index 646bbe0..5efd6e9 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -26,14 +26,6 @@ static bool g_last_ready = false; // Track the latest state provided by UART or the autopilot. static SwitchInputState g_user_state; -#ifdef SWITCH_PICO_AUTOTEST -static bool g_autopilot_active = true; -static uint32_t g_autopilot_counter = 0; -static absolute_time_t g_autopilot_last_tick = {0}; -static bool g_uart_activity = false; -static bool g_ready_logged = false; -#endif - static void init_uart_input() { uart_init(UART_ID, BAUD_RATE); gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); @@ -117,65 +109,11 @@ static void poll_uart_frames() { parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING, parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8); } -#ifdef SWITCH_PICO_AUTOTEST - g_uart_activity = true; -#endif index = 0; } } } -#ifdef SWITCH_PICO_AUTOTEST -// Replays the Switch-Fightstick grip-screen sequence: press L+R twice, then A twice. -static SwitchInputState autopilot_state(const SwitchInputState& fallback) { - if (!g_autopilot_active || g_uart_activity) { - g_autopilot_active = false; - return fallback; - } - - if (!tud_mounted()) { - g_autopilot_counter = 0; - g_autopilot_last_tick = {0}; - return fallback; - } - - absolute_time_t now = get_absolute_time(); - if (!to_us_since_boot(g_autopilot_last_tick)) { - g_autopilot_last_tick = now; - } - - // Run at ~1ms cadence similar to the LUFA fightstick timing. - if (absolute_time_diff_us(g_autopilot_last_tick, now) < 1000) { - return fallback; - } - g_autopilot_last_tick = now; - - SwitchInputState state = fallback; - state.lx = SWITCH_PRO_JOYSTICK_MID; - state.ly = SWITCH_PRO_JOYSTICK_MID; - state.rx = SWITCH_PRO_JOYSTICK_MID; - state.ry = SWITCH_PRO_JOYSTICK_MID; - - // Fire L+R twice then A twice, loop every ~300ms to keep trying. - // Hold each press for a few ms so it survives the 5ms USB report throttle. - uint32_t step = g_autopilot_counter % 300; - constexpr uint32_t press_width = 50; // ~12ms - auto in_window = [](uint32_t v, uint32_t start, uint32_t width) { - return v >= start && v < start + width; - }; - bool lr_down = in_window(step, 25, press_width) || in_window(step, 50, press_width); - bool a_down = in_window(step, 75, press_width) || in_window(step, 100, press_width); - - state.button_r = lr_down; - state.button_l = lr_down; - state.button_a = a_down; - - g_autopilot_counter++; - - return state; -} -#endif - static void log_usb_state() { bool mounted = tud_mounted(); bool ready = switch_pro_is_ready(); @@ -188,12 +126,6 @@ static void log_usb_state() { g_last_ready = ready; LOG_PRINTF("[SWITCH] driver %s\n", ready ? "ready (handshake OK)" : "not ready"); } -#ifdef SWITCH_PICO_AUTOTEST - if (ready && !g_ready_logged) { - g_ready_logged = true; - LOG_PRINTF("[AUTO] ready -> autopilot active=%s\n", g_autopilot_active ? "true" : "false"); - } -#endif } int main() { @@ -209,21 +141,13 @@ int main() { switch_pro_set_input(g_user_state); LOG_PRINTF("[BOOT] switch-pico starting (UART0 log @ 115200)\n"); - LOG_PRINTF("[INFO] AUTOTEST=%s UART1 pins TX=%d RX=%d baud=%d\n", - #ifdef SWITCH_PICO_AUTOTEST - "ON" - #else - "OFF" - #endif - , UART_TX_PIN, UART_RX_PIN, BAUD_RATE); + LOG_PRINTF("[INFO] UART1 pins TX=%d RX=%d baud=%d\n", + UART_TX_PIN, UART_RX_PIN, BAUD_RATE); while (true) { tud_task(); // USB device tasks poll_uart_frames(); // Pull controller state from UART1 SwitchInputState state = g_user_state; -#ifdef SWITCH_PICO_AUTOTEST - state = autopilot_state(state); -#endif switch_pro_set_input(state); switch_pro_task(); // Push state to the Switch host log_usb_state(); From 53f07b5a6fa04c54e6f7b11fe4931cb8e62e08dc Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 8 Dec 2025 19:27:30 -0700 Subject: [PATCH 083/134] Add reference to GP2040-CE repo --- switch_pro_descriptors.h | 1 + 1 file changed, 1 insertion(+) diff --git a/switch_pro_descriptors.h b/switch_pro_descriptors.h index 44cb257..2ec3444 100644 --- a/switch_pro_descriptors.h +++ b/switch_pro_descriptors.h @@ -2,6 +2,7 @@ * Switch Pro controller descriptor and data definitions. * Copied from the GP2040-CE project to mirror the behaviour of its * SwitchProDriver without pulling in the rest of the codebase. + * https://github.com/OpenStickCommunity/GP2040-CE */ #pragma once From 7dd8e414a41b01d16f09d7eb5f7d9caed72c1107 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 8 Dec 2025 19:27:30 -0700 Subject: [PATCH 084/134] Add reference to GP2040-CE repo --- switch_pro_descriptors.h | 1 + 1 file changed, 1 insertion(+) diff --git a/switch_pro_descriptors.h b/switch_pro_descriptors.h index 44cb257..2ec3444 100644 --- a/switch_pro_descriptors.h +++ b/switch_pro_descriptors.h @@ -2,6 +2,7 @@ * Switch Pro controller descriptor and data definitions. * Copied from the GP2040-CE project to mirror the behaviour of its * SwitchProDriver without pulling in the rest of the codebase. + * https://github.com/OpenStickCommunity/GP2040-CE */ #pragma once From b6553fc305ff70b7958431934410318b40194bee Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 8 Dec 2025 19:36:01 -0700 Subject: [PATCH 085/134] Rearrange python code --- .gitignore | 2 ++ README.md | 26 ++++++++-------- .../example_switch_macro.py | 2 +- pyproject.toml | 18 ++++++----- src/switch_pico_bridge/__init__.py | 30 +++++++++++++++++++ .../controller_db}/gamecontrollerdb.txt | 0 .../controller_uart_bridge.py | 2 +- .../switch_pico_bridge/host_uart_logger.py | 0 .../switch_pico_bridge/switch_pico_uart.py | 0 9 files changed, 58 insertions(+), 22 deletions(-) rename example_switch_macro.py => examples/example_switch_macro.py (95%) create mode 100644 src/switch_pico_bridge/__init__.py rename {controller_db => src/switch_pico_bridge/controller_db}/gamecontrollerdb.txt (100%) rename controller_uart_bridge.py => src/switch_pico_bridge/controller_uart_bridge.py (99%) rename host_uart_logger.py => src/switch_pico_bridge/host_uart_logger.py (100%) rename switch_pico_uart.py => src/switch_pico_bridge/switch_pico_uart.py (100%) diff --git a/.gitignore b/.gitignore index 0c5aeec..b95424e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,7 @@ GP2040-CE build debug .pycache +*.egg-info +hid-nintendo.c __pycache__ !.vscode/* diff --git a/README.md b/README.md index e7f5626..a66f038 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a ## What you get - **Firmware** (`switch-pico.cpp` + `switch_pro_driver.*`): acts as a wired Switch Pro. Takes controller reports over UART1 and passes rumble from the Switch back over UART. -- **Python bridge** (`controller_uart_bridge.py`): reads SDL2 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux). +- **Python bridge** (`switch_pico_bridge.controller_uart_bridge` / CLI `controller-uart-bridge`): reads SDL2 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux). - **Colour override** (`controller_color_config.h`): compile‑time RGB overrides for body/buttons/grips as seen by the Switch. ## Hardware wiring (Pico) @@ -29,20 +29,20 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a - Leave VBUS/VCC unconnected unless your adapter explicitly supports 3.3 V power output and you intend to power the Pico from it (the bridge expects the Pico to be powered from USB instead). 3. **Connect everything to the host and Switch** - - Plug the USB-to-UART adapter into the computer that will run `controller_uart_bridge.py`. Note the COM port (`Device Manager > Ports`) on Windows or `/dev/cu.*`/`/dev/ttyUSB*` path on macOS/Linux; pass it via `--map`/`--ports`. + - Plug the USB-to-UART adapter into the computer that will run the Python bridge. Note the COM port (`Device Manager > Ports`) on Windows or `/dev/cu.*`/`/dev/ttyUSB*` path on macOS/Linux; pass it via `--map`/`--ports`. - Connect the Pico's micro USB port to the Nintendo Switch (via the dock's USB-A port, a USB-C OTG adapter, or a PC if you are only testing). The Pico enumerates as a Switch Pro Controller over USB. - Any SDL-compatible gamepads you want to use should also be plugged into (or paired with) the same host computer that runs the Python bridge; the bridge is the one reading them. 4. **Power-on order and sanity checks** - Power the Switch/dock so the Pico gets 5 V over USB; its USB stack must stay alive while the bridge streams data. - - On the host computer, run `python controller_uart_bridge.py --list-controllers` to make sure SDL sees your pads, then start the bridge with `--map`/`--ports` (or `--interactive`) referencing the adapter path you found earlier. + - On the host computer, run `controller-uart-bridge --list-controllers` to make sure SDL sees your pads, then start the bridge with `--map`/`--ports` (or `--interactive`) referencing the adapter path you found earlier. - Watch the Rich console output: you should see each controller paired with a UART port and the rumble loop logging reconnects if cables are unplugged. 5. **Common pitfalls** - A flipped TX/RX pair results in silence (no button presses); swap them if the Pico never shows input. - Some adapters default to 5 V logic—move the jumper to 3.3 V before touching the Pico. - If you use multiple adapters, label each cable; COM port numbers can change between boots. - - When testing on a PC before plugging into a Switch, you can verify activity with the lightweight `switch_pico_uart.py` helper or the Windows "Game Controllers" panel. +- When testing on a PC before plugging into a Switch, you can verify activity with the lightweight `switch_pico_bridge.switch_pico_uart` helper or the Windows "Game Controllers" panel. ## Building and flashing firmware Prereqs: Pico SDK + CMake toolchain set up. @@ -88,7 +88,7 @@ pip install -e . source .venv/bin/activate # or .venv\Scripts\activate on Windows controller-uart-bridge --interactive # or, equivalently -python controller_uart_bridge.py --interactive +python -m switch_pico_bridge.controller_uart_bridge --interactive ``` Options: - `--map index:PORT` (repeatable) to pin controller index to serial (e.g., `--map 0:/dev/cu.usbserial-0001` or `--map 0:COM5`). @@ -102,14 +102,14 @@ Options: - `--deadzone 0.08` to change stick deadzone (0.0-1.0). - `--zero-sticks` to sample the current stick positions on connect and treat them as neutral (cancel drift). - `--zero-hotkey z` to choose the terminal hotkey that re-zeroes all connected controllers on demand (press `z` by default; pass an empty string to disable). -- `--update-controller-db` to download the latest SDL GameController database before launching (defaults to the copy in `controller_db/` if present). +- `--update-controller-db` to download the latest SDL GameController database before launching (defaults to the bundled copy in `switch_pico_bridge/controller_db/`). - `--controller-db-url URL` to override the source URL when updating the controller database (defaults to the official mdqinc repo). - `--trigger-threshold 0.35` to change analog trigger press threshold (0.0-1.0). - `--swap-abxy` to flip AB/XY globally. - `--swap-abxy-index N` (repeatable) to flip AB/XY for controllers first seen at index N (auto-converts to a stable GUID). - `--swap-abxy-guid GUID` (repeatable) to flip AB/XY for a specific physical controller (GUID is stable across runs). - `--swap-hotkey x` to pick the runtime hotkey that prompts you to toggle ABXY layout for a specific connected controller (default `x`; empty string disables). -- `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). +- `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `switch_pico_bridge/controller_db/gamecontrollerdb.txt`). ### Runtime hotkeys - By default, pressing `z` in the terminal re-samples every connected controller's sticks and re-applies neutral offsets. Change/disable with `--zero-hotkey`. @@ -118,16 +118,16 @@ Options: - If you launch the bridge with `--swap-abxy` (global swap), the per-controller toggle hotkey will show that the layout is enforced globally and will not override it. ### Updating SDL controller mappings -- The bridge ships with a pinned `controller_db/gamecontrollerdb.txt`. Run `controller-uart-bridge --update-controller-db ...` to download the latest database from the official upstream (`mdqinc/SDL_GameControllerDB`). -- The download only touches `controller_db/gamecontrollerdb.txt`; add `--controller-db-url https://.../custom.txt` if you maintain your own fork. +- The bridge ships with a pinned `switch_pico_bridge/controller_db/gamecontrollerdb.txt`. Run `controller-uart-bridge --update-controller-db ...` to download the latest database from the official upstream (`mdqinc/SDL_GameControllerDB`). +- The download only touches `switch_pico_bridge/controller_db/gamecontrollerdb.txt`; add `--controller-db-url https://.../custom.txt` if you maintain your own fork. - If the file is missing, the bridge will automatically attempt a download on startup. Hot-plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. ### Using the lightweight UART helper (no SDL needed) -For simple scripts or tests you can skip SDL and drive the Pico directly with `switch_pico_uart.py`: +For simple scripts or tests you can skip SDL and drive the Pico directly with `switch_pico_bridge.switch_pico_uart`: ```python -from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchDpad +from switch_pico_bridge import SwitchUARTClient, SwitchButton, SwitchDpad with SwitchUARTClient("/dev/cu.usbserial-0001") as client: client.press(SwitchButton.A) @@ -152,6 +152,6 @@ with SwitchUARTClient("/dev/cu.usbserial-0001") as client: ## Troubleshooting - **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, Switch sees a “Pro Controller”. -- **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `controller_uart_bridge.py` if needed. +- **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `switch_pico_bridge.controller_uart_bridge` if needed. - **Guide/Home triggers system menu (macOS)**: try different controller mode (XInput/DInput), disable Steam overlay/controller support, or connect wired. -- **SDL can’t see controller**: load `controller_db/gamecontrollerdb.txt` (default), add your own mapping, or try a different mode on the pad (e.g., XInput). +- **SDL can’t see controller**: load `switch_pico_bridge/controller_db/gamecontrollerdb.txt` (default), add your own mapping, or try a different mode on the pad (e.g., XInput). diff --git a/example_switch_macro.py b/examples/example_switch_macro.py similarity index 95% rename from example_switch_macro.py rename to examples/example_switch_macro.py index 703e2e9..87e8dfb 100644 --- a/example_switch_macro.py +++ b/examples/example_switch_macro.py @@ -1,6 +1,6 @@ # example_switch_macro.py import time -from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchDpad, first_serial_port +from switch_pico_bridge import SwitchUARTClient, SwitchButton, SwitchDpad, first_serial_port PORT = first_serial_port(include_descriptions=["USB to UART"]) or "COM5" # auto-pick first serial port, or fall back SEND_INTERVAL = 1 / 500 # optional: match controller_uart_bridge default diff --git a/pyproject.toml b/pyproject.toml index 85f8a54..2fcf4e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,12 +16,16 @@ dependencies = [ ] [project.scripts] -controller-uart-bridge = "controller_uart_bridge:main" -host-uart-logger = "host_uart_logger:main" +controller-uart-bridge = "switch_pico_bridge.controller_uart_bridge:main" +host-uart-logger = "switch_pico_bridge.host_uart_logger:main" [tool.setuptools] -py-modules = [ - "controller_uart_bridge", - "host_uart_logger", - "switch_pico_uart", -] +package-dir = {"" = "src"} +include-package-data = true + +[tool.setuptools.packages.find] +where = ["src"] +include = ["switch_pico_bridge*"] + +[tool.setuptools.package-data] +switch_pico_bridge = ["controller_db/gamecontrollerdb.txt"] diff --git a/src/switch_pico_bridge/__init__.py b/src/switch_pico_bridge/__init__.py new file mode 100644 index 0000000..22b5f85 --- /dev/null +++ b/src/switch_pico_bridge/__init__.py @@ -0,0 +1,30 @@ +""" +Python helpers for the switch-pico firmware. + +Expose the UART helpers at the package root so callers can import +``switch_pico_bridge`` and access the public API directly. +""" + +from .switch_pico_uart import ( # noqa: F401 + SwitchButton, + SwitchDpad, + SwitchUARTClient, + axis_to_stick, + decode_rumble, + discover_serial_ports, + first_serial_port, + str_to_dpad, + trigger_to_button, +) + +__all__ = [ + "SwitchUARTClient", + "SwitchButton", + "SwitchDpad", + "discover_serial_ports", + "first_serial_port", + "axis_to_stick", + "decode_rumble", + "str_to_dpad", + "trigger_to_button", +] diff --git a/controller_db/gamecontrollerdb.txt b/src/switch_pico_bridge/controller_db/gamecontrollerdb.txt similarity index 100% rename from controller_db/gamecontrollerdb.txt rename to src/switch_pico_bridge/controller_db/gamecontrollerdb.txt diff --git a/controller_uart_bridge.py b/src/switch_pico_bridge/controller_uart_bridge.py similarity index 99% rename from controller_uart_bridge.py rename to src/switch_pico_bridge/controller_uart_bridge.py index ebd2330..e223ed0 100644 --- a/controller_uart_bridge.py +++ b/src/switch_pico_bridge/controller_uart_bridge.py @@ -32,7 +32,7 @@ from rich.console import Console from rich.prompt import Prompt from rich.table import Table -from switch_pico_uart import ( +from .switch_pico_uart import ( UART_BAUD, PicoUART, SwitchButton, diff --git a/host_uart_logger.py b/src/switch_pico_bridge/host_uart_logger.py similarity index 100% rename from host_uart_logger.py rename to src/switch_pico_bridge/host_uart_logger.py diff --git a/switch_pico_uart.py b/src/switch_pico_bridge/switch_pico_uart.py similarity index 100% rename from switch_pico_uart.py rename to src/switch_pico_bridge/switch_pico_uart.py From d08d36df8294f11689f6d441de402dfbbaab55c8 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 8 Dec 2025 19:36:01 -0700 Subject: [PATCH 086/134] Rearrange python code --- .gitignore | 2 ++ README.md | 26 ++++++++-------- .../example_switch_macro.py | 2 +- pyproject.toml | 18 ++++++----- src/switch_pico_bridge/__init__.py | 30 +++++++++++++++++++ .../controller_db}/gamecontrollerdb.txt | 0 .../controller_uart_bridge.py | 2 +- .../switch_pico_bridge/host_uart_logger.py | 0 .../switch_pico_bridge/switch_pico_uart.py | 0 9 files changed, 58 insertions(+), 22 deletions(-) rename example_switch_macro.py => examples/example_switch_macro.py (95%) create mode 100644 src/switch_pico_bridge/__init__.py rename {controller_db => src/switch_pico_bridge/controller_db}/gamecontrollerdb.txt (100%) rename controller_uart_bridge.py => src/switch_pico_bridge/controller_uart_bridge.py (99%) rename host_uart_logger.py => src/switch_pico_bridge/host_uart_logger.py (100%) rename switch_pico_uart.py => src/switch_pico_bridge/switch_pico_uart.py (100%) diff --git a/.gitignore b/.gitignore index 0c5aeec..b95424e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,7 @@ GP2040-CE build debug .pycache +*.egg-info +hid-nintendo.c __pycache__ !.vscode/* diff --git a/README.md b/README.md index e7f5626..a66f038 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a ## What you get - **Firmware** (`switch-pico.cpp` + `switch_pro_driver.*`): acts as a wired Switch Pro. Takes controller reports over UART1 and passes rumble from the Switch back over UART. -- **Python bridge** (`controller_uart_bridge.py`): reads SDL2 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux). +- **Python bridge** (`switch_pico_bridge.controller_uart_bridge` / CLI `controller-uart-bridge`): reads SDL2 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux). - **Colour override** (`controller_color_config.h`): compile‑time RGB overrides for body/buttons/grips as seen by the Switch. ## Hardware wiring (Pico) @@ -29,20 +29,20 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a - Leave VBUS/VCC unconnected unless your adapter explicitly supports 3.3 V power output and you intend to power the Pico from it (the bridge expects the Pico to be powered from USB instead). 3. **Connect everything to the host and Switch** - - Plug the USB-to-UART adapter into the computer that will run `controller_uart_bridge.py`. Note the COM port (`Device Manager > Ports`) on Windows or `/dev/cu.*`/`/dev/ttyUSB*` path on macOS/Linux; pass it via `--map`/`--ports`. + - Plug the USB-to-UART adapter into the computer that will run the Python bridge. Note the COM port (`Device Manager > Ports`) on Windows or `/dev/cu.*`/`/dev/ttyUSB*` path on macOS/Linux; pass it via `--map`/`--ports`. - Connect the Pico's micro USB port to the Nintendo Switch (via the dock's USB-A port, a USB-C OTG adapter, or a PC if you are only testing). The Pico enumerates as a Switch Pro Controller over USB. - Any SDL-compatible gamepads you want to use should also be plugged into (or paired with) the same host computer that runs the Python bridge; the bridge is the one reading them. 4. **Power-on order and sanity checks** - Power the Switch/dock so the Pico gets 5 V over USB; its USB stack must stay alive while the bridge streams data. - - On the host computer, run `python controller_uart_bridge.py --list-controllers` to make sure SDL sees your pads, then start the bridge with `--map`/`--ports` (or `--interactive`) referencing the adapter path you found earlier. + - On the host computer, run `controller-uart-bridge --list-controllers` to make sure SDL sees your pads, then start the bridge with `--map`/`--ports` (or `--interactive`) referencing the adapter path you found earlier. - Watch the Rich console output: you should see each controller paired with a UART port and the rumble loop logging reconnects if cables are unplugged. 5. **Common pitfalls** - A flipped TX/RX pair results in silence (no button presses); swap them if the Pico never shows input. - Some adapters default to 5 V logic—move the jumper to 3.3 V before touching the Pico. - If you use multiple adapters, label each cable; COM port numbers can change between boots. - - When testing on a PC before plugging into a Switch, you can verify activity with the lightweight `switch_pico_uart.py` helper or the Windows "Game Controllers" panel. +- When testing on a PC before plugging into a Switch, you can verify activity with the lightweight `switch_pico_bridge.switch_pico_uart` helper or the Windows "Game Controllers" panel. ## Building and flashing firmware Prereqs: Pico SDK + CMake toolchain set up. @@ -88,7 +88,7 @@ pip install -e . source .venv/bin/activate # or .venv\Scripts\activate on Windows controller-uart-bridge --interactive # or, equivalently -python controller_uart_bridge.py --interactive +python -m switch_pico_bridge.controller_uart_bridge --interactive ``` Options: - `--map index:PORT` (repeatable) to pin controller index to serial (e.g., `--map 0:/dev/cu.usbserial-0001` or `--map 0:COM5`). @@ -102,14 +102,14 @@ Options: - `--deadzone 0.08` to change stick deadzone (0.0-1.0). - `--zero-sticks` to sample the current stick positions on connect and treat them as neutral (cancel drift). - `--zero-hotkey z` to choose the terminal hotkey that re-zeroes all connected controllers on demand (press `z` by default; pass an empty string to disable). -- `--update-controller-db` to download the latest SDL GameController database before launching (defaults to the copy in `controller_db/` if present). +- `--update-controller-db` to download the latest SDL GameController database before launching (defaults to the bundled copy in `switch_pico_bridge/controller_db/`). - `--controller-db-url URL` to override the source URL when updating the controller database (defaults to the official mdqinc repo). - `--trigger-threshold 0.35` to change analog trigger press threshold (0.0-1.0). - `--swap-abxy` to flip AB/XY globally. - `--swap-abxy-index N` (repeatable) to flip AB/XY for controllers first seen at index N (auto-converts to a stable GUID). - `--swap-abxy-guid GUID` (repeatable) to flip AB/XY for a specific physical controller (GUID is stable across runs). - `--swap-hotkey x` to pick the runtime hotkey that prompts you to toggle ABXY layout for a specific connected controller (default `x`; empty string disables). -- `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `controller_db/gamecontrollerdb.txt`). +- `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `switch_pico_bridge/controller_db/gamecontrollerdb.txt`). ### Runtime hotkeys - By default, pressing `z` in the terminal re-samples every connected controller's sticks and re-applies neutral offsets. Change/disable with `--zero-hotkey`. @@ -118,16 +118,16 @@ Options: - If you launch the bridge with `--swap-abxy` (global swap), the per-controller toggle hotkey will show that the layout is enforced globally and will not override it. ### Updating SDL controller mappings -- The bridge ships with a pinned `controller_db/gamecontrollerdb.txt`. Run `controller-uart-bridge --update-controller-db ...` to download the latest database from the official upstream (`mdqinc/SDL_GameControllerDB`). -- The download only touches `controller_db/gamecontrollerdb.txt`; add `--controller-db-url https://.../custom.txt` if you maintain your own fork. +- The bridge ships with a pinned `switch_pico_bridge/controller_db/gamecontrollerdb.txt`. Run `controller-uart-bridge --update-controller-db ...` to download the latest database from the official upstream (`mdqinc/SDL_GameControllerDB`). +- The download only touches `switch_pico_bridge/controller_db/gamecontrollerdb.txt`; add `--controller-db-url https://.../custom.txt` if you maintain your own fork. - If the file is missing, the bridge will automatically attempt a download on startup. Hot-plugging: controllers and UARTs can be plugged/unplugged while running; the bridge will auto reconnect when possible. ### Using the lightweight UART helper (no SDL needed) -For simple scripts or tests you can skip SDL and drive the Pico directly with `switch_pico_uart.py`: +For simple scripts or tests you can skip SDL and drive the Pico directly with `switch_pico_bridge.switch_pico_uart`: ```python -from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchDpad +from switch_pico_bridge import SwitchUARTClient, SwitchButton, SwitchDpad with SwitchUARTClient("/dev/cu.usbserial-0001") as client: client.press(SwitchButton.A) @@ -152,6 +152,6 @@ with SwitchUARTClient("/dev/cu.usbserial-0001") as client: ## Troubleshooting - **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, Switch sees a “Pro Controller”. -- **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `controller_uart_bridge.py` if needed. +- **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `switch_pico_bridge.controller_uart_bridge` if needed. - **Guide/Home triggers system menu (macOS)**: try different controller mode (XInput/DInput), disable Steam overlay/controller support, or connect wired. -- **SDL can’t see controller**: load `controller_db/gamecontrollerdb.txt` (default), add your own mapping, or try a different mode on the pad (e.g., XInput). +- **SDL can’t see controller**: load `switch_pico_bridge/controller_db/gamecontrollerdb.txt` (default), add your own mapping, or try a different mode on the pad (e.g., XInput). diff --git a/example_switch_macro.py b/examples/example_switch_macro.py similarity index 95% rename from example_switch_macro.py rename to examples/example_switch_macro.py index 703e2e9..87e8dfb 100644 --- a/example_switch_macro.py +++ b/examples/example_switch_macro.py @@ -1,6 +1,6 @@ # example_switch_macro.py import time -from switch_pico_uart import SwitchUARTClient, SwitchButton, SwitchDpad, first_serial_port +from switch_pico_bridge import SwitchUARTClient, SwitchButton, SwitchDpad, first_serial_port PORT = first_serial_port(include_descriptions=["USB to UART"]) or "COM5" # auto-pick first serial port, or fall back SEND_INTERVAL = 1 / 500 # optional: match controller_uart_bridge default diff --git a/pyproject.toml b/pyproject.toml index 85f8a54..2fcf4e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,12 +16,16 @@ dependencies = [ ] [project.scripts] -controller-uart-bridge = "controller_uart_bridge:main" -host-uart-logger = "host_uart_logger:main" +controller-uart-bridge = "switch_pico_bridge.controller_uart_bridge:main" +host-uart-logger = "switch_pico_bridge.host_uart_logger:main" [tool.setuptools] -py-modules = [ - "controller_uart_bridge", - "host_uart_logger", - "switch_pico_uart", -] +package-dir = {"" = "src"} +include-package-data = true + +[tool.setuptools.packages.find] +where = ["src"] +include = ["switch_pico_bridge*"] + +[tool.setuptools.package-data] +switch_pico_bridge = ["controller_db/gamecontrollerdb.txt"] diff --git a/src/switch_pico_bridge/__init__.py b/src/switch_pico_bridge/__init__.py new file mode 100644 index 0000000..22b5f85 --- /dev/null +++ b/src/switch_pico_bridge/__init__.py @@ -0,0 +1,30 @@ +""" +Python helpers for the switch-pico firmware. + +Expose the UART helpers at the package root so callers can import +``switch_pico_bridge`` and access the public API directly. +""" + +from .switch_pico_uart import ( # noqa: F401 + SwitchButton, + SwitchDpad, + SwitchUARTClient, + axis_to_stick, + decode_rumble, + discover_serial_ports, + first_serial_port, + str_to_dpad, + trigger_to_button, +) + +__all__ = [ + "SwitchUARTClient", + "SwitchButton", + "SwitchDpad", + "discover_serial_ports", + "first_serial_port", + "axis_to_stick", + "decode_rumble", + "str_to_dpad", + "trigger_to_button", +] diff --git a/controller_db/gamecontrollerdb.txt b/src/switch_pico_bridge/controller_db/gamecontrollerdb.txt similarity index 100% rename from controller_db/gamecontrollerdb.txt rename to src/switch_pico_bridge/controller_db/gamecontrollerdb.txt diff --git a/controller_uart_bridge.py b/src/switch_pico_bridge/controller_uart_bridge.py similarity index 99% rename from controller_uart_bridge.py rename to src/switch_pico_bridge/controller_uart_bridge.py index ebd2330..e223ed0 100644 --- a/controller_uart_bridge.py +++ b/src/switch_pico_bridge/controller_uart_bridge.py @@ -32,7 +32,7 @@ from rich.console import Console from rich.prompt import Prompt from rich.table import Table -from switch_pico_uart import ( +from .switch_pico_uart import ( UART_BAUD, PicoUART, SwitchButton, diff --git a/host_uart_logger.py b/src/switch_pico_bridge/host_uart_logger.py similarity index 100% rename from host_uart_logger.py rename to src/switch_pico_bridge/host_uart_logger.py diff --git a/switch_pico_uart.py b/src/switch_pico_bridge/switch_pico_uart.py similarity index 100% rename from switch_pico_uart.py rename to src/switch_pico_bridge/switch_pico_uart.py From 81f281e93f93d1d982178856b961cfe7ff330cab Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 14 Dec 2025 13:10:39 -0700 Subject: [PATCH 087/134] Update readme --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index a66f038..d48f873 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,27 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a - **Python bridge** (`switch_pico_bridge.controller_uart_bridge` / CLI `controller-uart-bridge`): reads SDL2 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux). - **Colour override** (`controller_color_config.h`): compile‑time RGB overrides for body/buttons/grips as seen by the Switch. +## Planned features +- IMU support for motion controls (gyro/accelerometer passthrough for controllers that support it to the Switch). + +## Uses +- **Remote couch co-op**: This was my motivation for the project and my main use case. Let friends play “locally” over Parsec while you feed the Switch a low-latency capture stream (e.g., Magewell Pro Capture) and the bridge provides the controller input path. Simply have the controller_uart_bridge.py script running on the host machine and connect the Pico to the Switch. Then have your friends connect to the host machine via parsec and play as if they were sitting next to you. I use OBS to capture the Switch screen from the capture card and just display it on my monitor for my friends to see. Audio is captured via Voicemeeter Potato and a virtual audio cable. See [here](https://vb-audio.com/Voicemeeter/potato.htm) and [here](https://vb-audio.com/Cable/index.htm) for the ones I use. [Note: Example setup video coming soon] +- **Switch automation (Python)**: write scripts/bots that drive the Pico directly using `switch_pico_uart.py` / `switch_pico_bridge.switch_pico_uart` (press buttons, move sticks, read rumble). +- **Twitch chat plays**: translate chat messages into controller actions on the host, then forward them over UART to the Pico. + +## End-to-end data flow (input + rumble) +``` +INPUT (buttons/sticks) +[Any controller] -> [Host OS HID] -> [SDL2 GameController] -> [controller-uart-bridge] + -> [USB↔UART adapter + UART serial] -> [Pico firmware] -> [USB (Switch Pro)] + -> [Nintendo Switch] + +RUMBLE (force feedback) +[Nintendo Switch] -> [USB rumble output report] -> [Pico firmware] + -> [UART serial + USB↔UART adapter] -> [controller-uart-bridge] + -> [SDL2 haptics] -> [Any controller motors] +``` + ## Hardware wiring (Pico) - UART1 pins (fixed in firmware): - **TX**: GPIO4 (Pico pin 6) → RX of your USB-serial adapter. From 231a38cb64b2795673aadbfb681ad7df37fec165 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 14 Dec 2025 13:10:39 -0700 Subject: [PATCH 088/134] Update readme --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index a66f038..d48f873 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,27 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a - **Python bridge** (`switch_pico_bridge.controller_uart_bridge` / CLI `controller-uart-bridge`): reads SDL2 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux). - **Colour override** (`controller_color_config.h`): compile‑time RGB overrides for body/buttons/grips as seen by the Switch. +## Planned features +- IMU support for motion controls (gyro/accelerometer passthrough for controllers that support it to the Switch). + +## Uses +- **Remote couch co-op**: This was my motivation for the project and my main use case. Let friends play “locally” over Parsec while you feed the Switch a low-latency capture stream (e.g., Magewell Pro Capture) and the bridge provides the controller input path. Simply have the controller_uart_bridge.py script running on the host machine and connect the Pico to the Switch. Then have your friends connect to the host machine via parsec and play as if they were sitting next to you. I use OBS to capture the Switch screen from the capture card and just display it on my monitor for my friends to see. Audio is captured via Voicemeeter Potato and a virtual audio cable. See [here](https://vb-audio.com/Voicemeeter/potato.htm) and [here](https://vb-audio.com/Cable/index.htm) for the ones I use. [Note: Example setup video coming soon] +- **Switch automation (Python)**: write scripts/bots that drive the Pico directly using `switch_pico_uart.py` / `switch_pico_bridge.switch_pico_uart` (press buttons, move sticks, read rumble). +- **Twitch chat plays**: translate chat messages into controller actions on the host, then forward them over UART to the Pico. + +## End-to-end data flow (input + rumble) +``` +INPUT (buttons/sticks) +[Any controller] -> [Host OS HID] -> [SDL2 GameController] -> [controller-uart-bridge] + -> [USB↔UART adapter + UART serial] -> [Pico firmware] -> [USB (Switch Pro)] + -> [Nintendo Switch] + +RUMBLE (force feedback) +[Nintendo Switch] -> [USB rumble output report] -> [Pico firmware] + -> [UART serial + USB↔UART adapter] -> [controller-uart-bridge] + -> [SDL2 haptics] -> [Any controller motors] +``` + ## Hardware wiring (Pico) - UART1 pins (fixed in firmware): - **TX**: GPIO4 (Pico pin 6) → RX of your USB-serial adapter. From 11b397df207a2ab3c33588ae45a3c258d08d6f9d Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 14 Dec 2025 13:23:20 -0700 Subject: [PATCH 089/134] Add firmware and readme for building firmware --- README.md | 32 ++++++++++++++++++++++++++------ firmware/switch-pico.elf | Bin 0 -> 644580 bytes firmware/switch-pico.uf2 | Bin 0 -> 91136 bytes 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100755 firmware/switch-pico.elf create mode 100644 firmware/switch-pico.uf2 diff --git a/README.md b/README.md index d48f873..fd3ad56 100644 --- a/README.md +++ b/README.md @@ -67,19 +67,39 @@ RUMBLE (force feedback) ## Building and flashing firmware Prereqs: Pico SDK + CMake toolchain set up. + +### One-shot build + flash (picotool) ```sh -cmake -S . -B build -DSWITCH_PICO_AUTOTEST=OFF -DSWITCH_PICO_LOG=OFF +python3 build.py +``` +- Requires `picotool` on your `PATH` (or set `PICOTOOL_PATH=/path/to/picotool`) and a connected Pico in BOOTSEL mode to automatically flash. +- Set `ELF_PATH` to override the default `build/switch-pico.elf`. + +### Manual build +```sh +cmake -S . -B build -DSWITCH_PICO_LOG=OFF cmake --build build -j ``` -Flash the UF2 to the Pico (e.g., bootsel + drag-drop or `picotool load`). +This produces a `.uf2` you can flash (typically `build/switch-pico.uf2`). + +### Manual UF2 flashing (BOOTSEL, no tools) +If you already have a built (or use the pre-built one in `firmware/`) `.uf2`, you can flash it without rebuilding: +1. Unplug the Pico. +2. Hold the **BOOTSEL** button. +3. While holding BOOTSEL, plug the Pico into your computer over USB (not the Switch), then release BOOTSEL. +4. A USB mass-storage drive (usually `RPI-RP2`) will appear. Copy the `.uf2` onto it (drag-and-drop). +5. The Pico will reboot automatically and the `RPI-RP2` drive will disappear when flashing completes. + +Tip: if you don’t see `RPI-RP2`, try a different USB cable (some are charge-only) or a different USB port/hub. + +Flash alternatives: bootsel + drag-drop or `picotool load`. Flags: -- `SWITCH_PICO_AUTOTEST` (default ON in some configs): disable autopilot/test replay with `OFF`. - `SWITCH_PICO_LOG`: enable/disable UART logging on the Pico. ### Changing controller colours -`./build.sh` now writes a random colour into `controller_color_config.h` before building so the Switch shows a fresh colour when you flash. -- Use `./build.sh --color FF00AA` (or `--color 12,34,56`) to pick a specific colour applied to the body/buttons/grips. -- Use `./build.sh --keep-color` if you want to build without touching `controller_color_config.h`. +`build.py` can optionally update the **grip** colours in `controller_color_config.h` before building/flashing (default leaves the file unchanged): +- Random grip colours: `python3 build.py --random-grip-color` +- Set grip colours: `python3 build.py --grip-color FF00AA` ## Python bridge (recommended) Works on macOS, Windows, Linux. Uses SDL2 + pyserial. diff --git a/firmware/switch-pico.elf b/firmware/switch-pico.elf new file mode 100755 index 0000000000000000000000000000000000000000..fe40183825665e00cb9d03df88c1f898be5b6400 GIT binary patch literal 644580 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%0DFNJd&2nWd`?K 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

R* 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

yZQh2y(^3%_w!^b;}-ZFp#bL&7*gCHHg4t? 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 Date: Sun, 14 Dec 2025 13:23:20 -0700 Subject: [PATCH 090/134] Add firmware and readme for building firmware --- README.md | 32 ++++++++++++++++++++++++++------ firmware/switch-pico.elf | Bin 0 -> 644580 bytes firmware/switch-pico.uf2 | Bin 0 -> 91136 bytes 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100755 firmware/switch-pico.elf create mode 100644 firmware/switch-pico.uf2 diff --git a/README.md b/README.md index d48f873..fd3ad56 100644 --- a/README.md +++ b/README.md @@ -67,19 +67,39 @@ RUMBLE (force feedback) ## Building and flashing firmware Prereqs: Pico SDK + CMake toolchain set up. + +### One-shot build + flash (picotool) ```sh -cmake -S . -B build -DSWITCH_PICO_AUTOTEST=OFF -DSWITCH_PICO_LOG=OFF +python3 build.py +``` +- Requires `picotool` on your `PATH` (or set `PICOTOOL_PATH=/path/to/picotool`) and a connected Pico in BOOTSEL mode to automatically flash. +- Set `ELF_PATH` to override the default `build/switch-pico.elf`. + +### Manual build +```sh +cmake -S . -B build -DSWITCH_PICO_LOG=OFF cmake --build build -j ``` -Flash the UF2 to the Pico (e.g., bootsel + drag-drop or `picotool load`). +This produces a `.uf2` you can flash (typically `build/switch-pico.uf2`). + +### Manual UF2 flashing (BOOTSEL, no tools) +If you already have a built (or use the pre-built one in `firmware/`) `.uf2`, you can flash it without rebuilding: +1. Unplug the Pico. +2. Hold the **BOOTSEL** button. +3. While holding BOOTSEL, plug the Pico into your computer over USB (not the Switch), then release BOOTSEL. +4. A USB mass-storage drive (usually `RPI-RP2`) will appear. Copy the `.uf2` onto it (drag-and-drop). +5. The Pico will reboot automatically and the `RPI-RP2` drive will disappear when flashing completes. + +Tip: if you don’t see `RPI-RP2`, try a different USB cable (some are charge-only) or a different USB port/hub. + +Flash alternatives: bootsel + drag-drop or `picotool load`. Flags: -- `SWITCH_PICO_AUTOTEST` (default ON in some configs): disable autopilot/test replay with `OFF`. - `SWITCH_PICO_LOG`: enable/disable UART logging on the Pico. ### Changing controller colours -`./build.sh` now writes a random colour into `controller_color_config.h` before building so the Switch shows a fresh colour when you flash. -- Use `./build.sh --color FF00AA` (or `--color 12,34,56`) to pick a specific colour applied to the body/buttons/grips. -- Use `./build.sh --keep-color` if you want to build without touching `controller_color_config.h`. +`build.py` can optionally update the **grip** colours in `controller_color_config.h` before building/flashing (default leaves the file unchanged): +- Random grip colours: `python3 build.py --random-grip-color` +- Set grip colours: `python3 build.py --grip-color FF00AA` ## Python bridge (recommended) Works on macOS, Windows, Linux. Uses SDL2 + pyserial. diff --git a/firmware/switch-pico.elf b/firmware/switch-pico.elf new file mode 100755 index 0000000000000000000000000000000000000000..fe40183825665e00cb9d03df88c1f898be5b6400 GIT binary patch literal 644580 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%0DFNJd&2nWd`?K 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

R* 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

p=bly6rQ7;G679O79Q) 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 z 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𝔐iuK4vguQ?;Qrug>p+yU#1LwO7#cZ{iPoo`u)&v&81h&!C2m7(0>NjjoALD z4PEV5Ov1?jEBK!U?=bw0vogB)6{j%rt3CG^EV^P9TKW?PzZ%N!ojxARtYmJo1pI)( zZvx+Iu<}3A_&dUP8hu#{zp&=;xLI@vU_XAme(g5&3!n>Idc(~3Do^#{gKMsv zKU80f)gFu0A0B&sRsUKn{fKxpq!f<&tC)$NK09gg?K$uiH)8+Yv@xS!0sU#^KgZi= z)pyLuOZ}l3i+pg+POjzKUEsA2`T6iEL;sxeoKP9C#GMdzlU#S`ePP1;{2wVkDfj|nY)nl*I?MX z@^2Wr%3qW#zn!CBXRyxa9D+O={ZLps2 zJ`TTrNf!APBhvEoKEJJRxBXt45E3`tAhk;BT*)4hF={4AFIEmr?o ztnp#7(oDMLRM`rY6qfiC$zZs?MiVqWsW)swsp{wKlxpg4XwjL|Eq=t{phr~itL zDXQpg(jNi#=NUZz6^5?@u2vbE;;FckM!4r7jr}9tA2{3>FIvF{Lo<06;IRC zJ-^~FK__6VJ)8=!&oD>Armr0ZC~nEBun zv@c$8d&GaB3&^)BmIRKqVIwg z^E1|W&)YJ(%2TY*$SyIhJuksb| zGo}|-yibc2Z!==$_prg@SNu&&-(#@oipOc`hn<(tw z)qfWAd#r$c|2>|g%RX3s*$0bdPdxVaOZsH#vL6xuA?=S>v}gC4`hObwePC-Z_89su z=syKtNG4H!Cl+J*vy&<4JE@=MKk4uN;0wVg2P}E6G5nI}n8Bhejwc^nUO#sm?E2GS zrQc()=#LvL`cnpr{>St~j6;D-LMHuD&A~-a~nur|$LZEB||9#Rs+gw;22j`0t>;Q!qq-TJbj{ zzuK?(n-QzLdkhv`aX2GgSn)R_7XQ_y42!P#o0fjq&WOFf?i-ER%SUlIE&o&wtNxzu z<*)Wf{F@o%uXvoE?)&qwu?(wzijf)V!is$vvD))PgGE;?OiSnddauXxlUdrx4_EAe z#e(#=W+yL#{sOQaUy2PG>92x*B={U|#Q9qB5hGpYE1sdniZ^Jn;s{23>5t(biN-t6RJ=wAlwMzmK`hW=6Ly*$5;^R3!@t9f1_ zc`KG*%un>)Ijr=aK0o=~4E0egKTq#T{uKJB!2R5a{`x(Jeh>65v~L!4$^R?hTV6qW zZp87bSb#CV??ZnO?EODKe>`mHs=s0cM*d&I{|I;yH)8ou8M^otLoo8c2t)o1ST`a+ zKQ`&o#jn_cd~o$7$H9L%crhsQ4;s4Ux4`I=(kt#CA6&lv9FFgG5Ulmbx%9std&Tvn zV*h!%AJ3wD?Db3X^VsXJlb^dWKUt}XDO^!s6dTarnyugZCx?U29 zz2qnP`~mzs_`P7%=l6i8l89g6#`B_2#`L~C#ULa(*X-oe@IMDW%#`;;Eu;S#^m|Qy ze-7S?ykSqIpMMP=Gx&Sp-AU4`0%j#IaOd$qNPRu_};P4-R;A@nfW~nEWJ9 z#WxgNGG63|;b{=w#`Y-wuOCe|&pJ z7k(=Dd(=gTXYvr8-#7O1!4<}1&fo3L(Rs&ruVWAPV3)zk;hv(uQND_|=d>G4qbQ-_w~HuV(=KxZ>>MF^ZHmHZx2NG_4W9s?s%J?d2>7O4dSRRXt0w(p@C(QEPk?vj(hJ-4cbfEi_pESCzh5|) zUO1+I!OQ$N{f*;evGE^Xnxh{L-xG$;Z->#Ec=_{!i;TSyf4{+RnjychE{NmJ`!7=l zubzSbXk+iz&p?0N>_5hu=~M0741X4{iQkJUy?GvcF*u9YGduYMu=Qs@lfz#G+w=5l zuYUi{2UqYXXGM8?`>OpfM}~ec=?n0yL*(zt9`xtZpJ?z;;a`>GKiSY#{(6J=!+%ze ze}kcm{{n+w#t(NnGd`_87aF?ws|FtjKWE0L@iz=z{FfTM3jWJ;{FfQJ_^&p&4F9z` z{vC!c{u>QuEtz~a$G_9i#eb{8AA|q)9RDsu7yq3G-vR&KIsPd_7yknWe?$3W&wc;; z_4zJi-(CI&|4jMk_@@lr2Pt|Ipx<^240Ra{P}Py7-?k_&E6Y=J=m7 zbn)*qcoqD!E}h=q1PmLO*Z)HdemndNbNmYoUHLCE_!9V!%<=Qmuxa@(F?c8Zg&cps zp)3Cr4gMzlt8)A&8@l+{8~gdGlTcHuXIcJha&RL(0Doyn2OCul$#T&wH^yzasj2@Ku~=SQE-C zf@htc(QDuzzto?y$3k@YYUjcR)81sdrzwE+It50 zN5^LEI~QDjQ`Wu;_)6$eK9k_%jXqri{@UApc`E-i;J-fCpBGd4w}H=Q{j?&K_apGT zk+=Lq@&6Q@_0Qh@|NeZ=&F z#N##40y=M`z_#EX1t#T=1g=~pWg-6dfLmAcz)pPIjBn!DW8biUv3|!N#r$jZ{?PnItcIjx>c0j2AoY7)s9y(s zJI`Mv56SOx@Kr{CJ^(H*&-CkB@Yk46V*Nf3X4+MC_%k~hg>dEUjDHHcmG9r=@WbGL zdt26@KL+1!^z|8Vk@`e^Nznfv8T!k>1?*EC53d9NEYwHsSqgpxe}Nd$u6@4?w)NQr z-eK&=`@j$3FFY_y{5{EaU_DQrqJ6Rlp97!8{1VH%9eku2&-Z}$-5%w!cmE^cs~BHB zq*s0Z1+4uQvUm9%O2gi9hJP=)=&uDgOnug>;jf7Hc<=sm!1wVyYD36x3s~#H6=bOT z{cjA#?dX%rlRj0U+xB*p-st1y;EtIeuLgh1^ylZnbC+fP`6ck-$mdd5eoyiccnJ9q zg!&L$Be~y)Jo>7%QIz$=S^JMd~a2qWUg^emwde=i4`eGyAc3|INqY zAND4_+@(^7Bqu|E^2ludJ9irYOOjFW=dm|*zbl_u70H8VW#f}r70Ep0ITh$P3uAwx ze%}qw>{(CpZKXH;{S)wgv_IO%=fFqcFU0VgO5O-(cb+OT%bM|g!2Ctd@ScR*E`&k%ziyzjD0^0{E)GauL0kJzcB|k z`5Ob^ojc?Bn46pm-g|bY&+h=g1OGMZ!yfQyobP6uclCV=Y{%DJI_gJ8-(Cqmg83kh z&!fRs|NFq!UaiQ{PXRy0_!OJQ(*=CFjry$#ngehLJ87w6XwaryTo$Af=%Sf-x^@P)_^GD7P~R)If{JXm%)d7J^B zGUN4v9KHyA`$FDd8TdZ{UdVVjHQ*0{A4`(nwGQ_rp9XUUdp;{U^T_G*)t8`um-%7} zCh5;z;D4-S`g%Y3Sl0g$3}7#D2u_GX*}T>EC6g`u`2st}nh1z6N`z_i~B;J4>)<8!~&*2e$mIEsgwMn5+h8 z_UVx1v>bm5J`#QJrT)r)D|mw$50`?iKXx5>9`@~y!2db$UHIb@0spHE3Hm<8pM}Zu zlP^LaM}Nk^%KsZ+E8hpfx4qQQZ<618-@tiTeorZR9f^JT1F+@44*VegR-C{77(9eL zG&g2pKb%&_I(9>t*QUtgONrre`&-^lE-rVKL_(n zt81^G1#7?C>swDU_l-QC#lFVz{5#;w&3x7m{w((86!MpR-Xd)3yB54~MJB)Vz_z|4 zIsS`s^h>~Rpnsx0{eAE%^zDVAypMyA!M?u}to}cViXMS{Vtvo%i!Cn&U(b0ZeiPy9 z%e#M)Su2wT`$B#n0AFtW|Br~yc$Yks|5tK!e5vGF#;>!d(3$I!?Z^YO;qw1y@ZSde zuk_D=KTiG+1o}JiH?di+zeS%{fY9x)z55q}zl=SO`mhMRiu1g&J|}>!ez2BIe#!cw zC**%FxWf88j;{*X>fhzyY`yrRhbLrpv!B#(sy^&mlzD9X`0<7oHQNOG~0-o4(( zKUDsEpj&-<5_~)Ij`rgPOKBJVwUhkB{}UA9*yDYFtAAes{XVlkE`VKp4dg9<=1pMh zPYr>m7>_4AdQY+uyw~XWR&X}o%}=(0=Nf(eBk&RPvi0e1@UisA4p+aPI5tB0BP=W<0+dTwy$(9NN1Nd0mOTqJF*sx|P=n;KPyEWr6=> z@J=J|tvUWq4!;*{?aN2Ow@qa8*{8r|<4@cLe%R>iZm{B2#r}O3yo~jE?7tq?JI`Sc zsGjS;*MpBiKDwd)JQ@6M^!;T4|1MbZt73i^fv-O{SyJ+R`n#`0dk!p+M6aPX7t{~Z?c>jxK2|EFNPpIik$cV4!CbqV+} z>;>KE+WUv#^YHhg{Wu9hU1jE{KY>2U`bg_r$>&b+?T7ID#ekm%&ocWVb5EGw{+HzN zE5J{ppC`KVdy+STzgx`Oa|-w@)_?jvgUUM%{4?~OZuFQhU#9&lz#4DKaBCnfl}C%6 zYCCNdOQmut9T_W+Y;ASM$IGR@O?~y`=u&w98{gArxm9lSF27o-%DerJZu6?ZTCq8q zR%)a5bgW!-Z~Wu!aOp^6GHn<2%D%K*YnPkpHiuwLoBhdHy;@3n@f&X+Y?k|VbGTk_ z^YXq#ua;9{YSv0eE7fY+F1IKqqH4X~NE+2*d$ishSJutRZkp5)H?@l+TQ^lME+=ct z#fJMb(rj0#Js+)hXLPi0BuUeuwdtuFSDikTo-*{-b!Vh$(kySOv{cjfcsf$$U3INw zv{@W4rzK=Dnefg!-n7`)Pf~N6s`JjoVzXJC43#l&V(c4fG?GrS*-p2V+mw0!`ohv$ zxzlbItE6jm+S_z*V4zj5mC`2fzT?e?j@=co;j}SFE+D2r9CfcZbgz00)WR}X0hcPv zvaq~SM{-$SEtN#^2i_kdgHn0opya|}wb*K>!;|fD+OCY#od?OMuym~0O1sPl1e#=w@e(?a5{cCoFTkx#X*d&B;glTTrAi_dnv-a(T}F{LZ2iONkTUgl||Sx-yN z%C>UTNnZJ_7%y%ur%~vxoFH`cXM3^Pm3tfYt+(2Uwe1R67DPT$tX79nDBWCI=rqvJ zavI8OmK$|C*Y}E=;Oo0|i|<|AtZ6Bf*NSR+w0(M|c5ViW69!aj=UIvaE1Q)qW8Fjn zg;ZUdS$)aALxuyy*-h^ulA0O zMIj9oTpvhoX>p{@3qRYF>3B;?3(G65z@J{!DR+Fgt#CKhV`3?Iz4w`JIBlC3{L0X; zPP8Mg%bl{UWYABdD;2K*C#Y(zG4-xtDhyJBMnt7p#R7QNLTD+)`634B`w=XiR~}X6 z1Y4-o-0SDcQJPi9yafTsICXnvb=hJ*QAtM; z*H{{*Q5cjU(=K7d*m$s9L48Kc#dfDzK0tp8{mx{ibl;Y zfs9FO(UfFMqf%$~iu0eF$n-ALG^KToVy!a5t&wW6GR|jfvetzNQ&62$W11Y<3cb?2 zNLa*zP#N#C?CU_HS~<6Yx{r6-D_N}a{#O~0R<&Gi(86}5nsyDmzgeqeA}e&1XKwI1TPvDG zH(JWwYvMGTNz!mOu}Wo~b|%rsGA#04DmWR=+)!gTTX$;C!}6e1X=xm!!(|LsIjxQ~ z)9tjh?5XanyUA;tTj_YQwUu7Rb8+&K!Pc1L-Nof*tyoQ>6~OKx=Znyxs>D>m*W)v| zUwoUWx1=>qNoTyAeZvQ-Wg`uJbn{;1a}>kuyb@Oz<<7i|GBA_UAC#AWzqB07@JeLY z6eUhhS+{vpdg|F{tWH;-dG;BblMQEXa9_};)$7mK-_82Fap;_(jhlw}cHoPq$#>Jb zGt)Ke&Pi8q+L&$}TD9i9bko{(r*2M9J#%Badez2_=S|~Svu@+&13AbT3zfE(5ArW< z6}Mqq=W+7 zBUFg*v@Ys}$=$bE)1neNxJm@~!ehPiTAJ+1CE2%HvBo&6He_^ybW5Wn;|&yQOQ+q6 z1-PD)Um4z*JrW5sy^fmlF^%ci_LN#A>(1P8=Elv5fg2K8;Z{+ysc6XPMHNpf^qs|c5R64FFcEx6v~FSRJR zKIN~zyPhq$o-MeZEvRQp<0^YxC5{WaPOc6(_24fF2b_`&DBDs=%q9O#B;^UFttC~w zlv3S+(aHoCx1E+QE;s8*b-Bxnza-%A(u2I1oc{6e56?Z&>tpqX^8y+S(0$qgTPvd_lNGeiib@((Y1EHm}-H?3)4MdJ+{SsWeO86!<@Y=IfZh%P#n!%E4L84wkQ5A5GuWa-7i8)YnzZQ1u6sXJt!` zu46oCW1@jiUSwv)gYeZ#$`}N^)ZtEbE8bPP5pQ(e62D=9C9c$iAC@JSF^v^YReUN( z4Kq7jqVzaxd(YPU2C5hyu3P9bbQ+ED-7kdfog}a_rd%4IzEay(*RqLurlpmQWJas% zBW18t2)Eqp-eU8@ECWKG%`q*|0tm|((sml$8g3yhxn_7hfx|1huMWOPC@d7t!hjsu z<#Ck^H)*`4*$!1|<6Nf`c+ePc`E?miN7~J*lUm}xmK*d*GFH(FKx} zzO*ll1oTuW8zo{}iYirTCl07;g>lFI!=LWnO&QJd!({gziCUMi=d86z;MZKvu8V1a zo@-3XwVj62ZD?yqgjzXEFi~l+f-1Va2SPsbV(`A3CH6q@xt%}bOF&0hW*&q%g4d_D zbJApqK&4uZYNb71nZRplm4?U3u{9$|Y%Gtr_PYIIkVCsC1Do;kuh;X_ib{UnQr$A z`L|hL^o=mFx%rJvqGVj&vOXEw;|(=vxVcq?a+)_{pv1L9yZ|x0Z#3@snG)k>2`-Pu&WN z7eBeRG$f3l+#-&%OQ_G%w((NhuC|!t4_KDDO<>ZE zYMQ`OKlZzuqFTtg;UDfG8yODQfzy~UtWLe-ZmJTR8!_EBYfrD2C`pt zW<2dqDP^cLY$XnEZB%JfJa{v?@!BPE2w!bUSR&jtpB@ z+gYtqEpfs&inC*E?){hB$_nyyGIJ&i-Ni5R?!&qbr%5nOA|Y&d8MDPbhqU~nduM|4}|B<55fbM3vqa;brGwb2na zv!ss<)DFfN0jK9v`>v|s;EgoP<5-0a({9(;OpdDNW^q*jf)ffYnn(|l`WAsKY8`q_3nt#Ui+YKe1nFOqw#9bM;iOFHt4$c(-I zVtZsPb+TqGV-wt-E@Eb=YX45#HR^r4OX=No|0vh>Pi)T8g8Rl=spOsm{@0aoB}%B` zf+CJ-+y^(7jZcQ?z|uIPwZOn!M4fXZ)(zF128_sh^0e440sL|ZdAXZf?0KQfKpjB3%k`SjH(`&fuw+NWVIds5+f zw{i}QTJpN3gI@;W$x?OEYU35ZH6!r^kqp=63F4JUGl>6I#_NM;yfW2@p22zi?eSi6H(@HHD z-Sby%9ctIm)oPkol-KT&Qqy-;t_M4n0WAIb1^W;Q)8ihpv8NO5e!Je#Ndq^8m_))N z1-hQrudj?_ESNyF5_Ox#vO94+OlJ4wr>D*7+M<3MsY{D^MBClO$Zv>uYK%@Eg=$us zKK(Xn|FlkLncA+8u%ST5J5%J=yiTBD8D)Bz{?+_9;5(Vj!#p=$oJdt$zk$$IpcPI4 z42~Ll=!HS`SsY>S==14S>&T*T(;HwXn9KyZEeq+5mUC|S=aZ{rRR8G5kO9i;6|3MC zYdue+XVj}oE$EdSgU03^uui~_ZOSoVk>DyPqu{5atgddu#;w9j&eu*m4Y8gflQ~ zX{xf^7k_Z_u?RqD=uc?+h2;K7(ogT&EhFFNI5jeD-kHcs1*%}z_Mh#8-LN$rxUPT{0j99e!g z2rYD3soB#_Hxtr9ouO<|FkI}E@T|)Ixv{LNdMcY;d>OJuez^oO7;!huAwCQsAt$U0abCCSB(` zN;<$a8EvdKmAuo`iuVd9WFkV9n=)gPm^(m}?V|e`^S@*DBXLMYg5zm?&d}7EZLfP8 z?}t$^SfNpcLD$l*<7j)*ar${k4+Y$&OIUx8=(OkM`TbX>^8i}D94pvfFaA#a; z(WlIoj^u|12es_(b&%gQqZE`)roqjlaf8~Q35fQsivDuI((QR`<(2PU@4J~35Lp)% zx25bRJ7-lXr@7*qljAmhcUZXDvE*k%oY|-;N!PNlFPt}i#gU6T6} z!r;q?kg~R;apSw+I`KpF;CpWD?N+penpqE=3Mgaao3-mK3G{3y(**g3Ruc}g4HVm3 z==X;EmTRGYl=|jI*by8G%hduBIXG zPa((E(}q*GX&a8`JSc(lUF=3gz6;$No})$RNw()`w_MP>agjd zb9u&Xj5A(|jpvJ9x@pf?x}D`VVpXZuHt985>nq?_>cKhDB7~)$n>XFe?=oazR$?WQ zZ$eaXudGIY>Ui}kB5qE(MUFhqoH}(hOFLuNb;ww}g$)l4_b^PO53Hfy@_w`1xHxBM z^=3}6ZC-WC`k{DS!9FYMYMtND4tt&d!Lt-&@2RUBK5;L`S?RD|jwXjS86#kF=FKKI z`fkb1+9MdcN8ExN8+wcE9QQ^E4MA4hsLMJ=Dj`+f_&h zB;4`Cc&afLL(QTyGP5Rh=P&I?(mBSf&_j&y7~lUwNlnj{6gQFcM_~Q#{eLJ4oM<+V zw16wsw{uvDoqOlhhJA;eqD76Ass(X#Y#|&(ann;A*?yulPpZVuX$5VlAlI42kolCD z7<#p?bJLu?@X8$f*>8Mw4~O_Z^xM<_xxMJ_o?+PV)`rE!M&xnz;&v19hr5TFGIe49 z79X|hcT_Ma(+=wE5hdH9=TG|`1m~dGkMgJIu;gX^lxJ+i47(G)dHRbo@q|ALnnP_t zlRmRbNcs+VT+xSbil-2M!4;pcvRZmA&KYY z_dE*FZ>D>`9y$D{Xu2dSS8m{QH_e~OcX{N~XUMPg;*2rY@BM*Ka;f(6ZaCW8?F7EY z_9A{)KcD}cgVyh3mXFvz2W!V4g<@1LzZiD%v*q^cKG*-`KYh_~fIrG_aRhwE_WrN% zy+6?U;Ty{FiTB>X7fBuXo;)e^wxK<2zKeHw)|}+U*Xf(TV!1y7#_ysuuAh@ES(Dv~ z-~J`XxAT#?$z{a>?e3)yyCYb0$-HN z+aG&DGWo(t&H8k4;EU-W{&`Q*&<~x%74=|QF26O4=Oybi@wvJycm1Co6Z5&__<00N wAzlsNPo3-aY60K58^;>o;Jaa7@(zAC9WUjhOGCyKlRWvi^OB)RgciU4KO_PWN9 z45%{?B#dT3MRA@-K}U?n(UE9IXB<&yyq&;2S#)d=?HL`l$E~xGe!q2Z65?cjzxVV0 z^?p9#=5uqa>ej8kx4!3`I(6z))oQ+2`=b>%JW8yD9(uxvSJI5j_3;NTkd|VP&AP^0 z?_+KIxq5G>*RXNT#u*-BYw)tRwHp~*)ZVbXpu*^7kTS1mKh}w+gh)w!d%XXWqppPj*4ZZ^wJztD$<7U zFsDc^5lC4|d1{5)D|Sje0|JBpy&+jE+hW*i*cy+09XH%=xSee! zgmvPx41}l%k=;mac0#^I+-@aD@!fj$7bJ}PqsJZ1*WNuEnSg_)HAH_-4*FS2^t~-@ys}(Xp)oz6Kb>^ipl_+r%LKWH z6cC}lOy5eMp(iaz;nBB{0^F@ol!KdJkH^l(^{s6@z1N#TtS{e7^m|Sc`z?G|hBCaj zsz*6^M&?WKGsVm-lK;JM3*TA8p7fXLTja!KCpP-rL_NmgFO%?>;e$u`Ulx6NK<3%J z=h&W)_c-Q&7c#W`^dv)=6R+A${@*QF=T{Zq!q;cWqW?2M?8G4tGfy8_|E{5@ znJL!f_kPG6uiHUv>afz&RMn&$IHM29JO(c_iz(LTecPotNm@xz=G3>uV;w+C-*V2T zS*np)2^)Q`kF`OnM{n$9Z3H*mkyGHz^Tp-7A5seLyGBvQd00r=+#hK{wrNA5!s1)o^qFg$fD|j zN#!nHZ6i_JAjwq=i4`2LCEUB*8}u~qPYudM1Yd|~PXrN#~Qv7c5TP9LMir*=IGiARiNTT`y5;Y9OV<+REMB4b< zAY&+2Bl1-i-QWM8OOu6`eXv`lug2pHA_`)JGG z_#g4t@=N$|%WijCV7G^{>6}~0ni@9eIR6x9<F?JZS3#6@CHRq|} z`)82yBvRy8Jw2RasR>%0E-PoKzO3+9O86_s;D4q@=j;s7n(Oo*MtBSAX|-5Oo&M_( zr}cKta6AohTC+=r<896QX)oiswq|Sgll)PHuDcsgFm27wsVBt}&))q7^M~eLR=Kl~ z`@p~YoF#;vVBboXh5Z!EGp)u5Es_fOXxzCT+d z+)p&g4`?(qJ(m^!DhYqp82ruqPq?n>oZ+EY(osP2t~pWtS&dnnG_}Op%$c~)s&jGg znCq&}Y2uZ1I7p?dsW~az?zAv=E@SEWYO}_%!-~6mU4@-!GjJsxGLl}-?bvfSO3HRP zpFl}_b{KK*0hguIfNO(`bS8);8JQ$2>rCbn+=q6UH61%Nj?|seQ?K&P{L}nVv<4aE zxrl|l9x)IhVkBY67$JdJosbQSA>W-iA-1e8oXc@8#kmsawK(-Hla@Ns3Q?)Hq;I#w z;SVm{?ALml0w4Q{LlaimPMa<({M8cv>M{5mGL-I>hU6NVJNY`Mq^DU?@J*MfPosY8 zMLDh`O!UzqlBXMO{jcdaK+{qbPU}2Irno!SJjP3UnXLcoNIdq3c#5cj>06UMyP=8g zg7)}bWRe)Z587eHb~4->15KC2%ANdqq4`iIy;g zSQXKNi%MuGaoQ7bGbO2heJd+!5&fW#4ah6$j0gFj*SuL{VWTPy=QK`!cQ#|z zShzn1lr<&nCb8czQE6bD{-=@f*NnkG)tSL@HD+e6riA;#oES8lU3_X#(!)mTP!-?; zW9x8V;D>eigEEXL!#KW-KLunzz+rKXRE8qJ2Ph7-jJ<*X`+NMKu>6l@{M&o@Tb8(q zBh|kc`k{EDKR~Y&6?^(!OF8t%mAFMQf^O&8x5eTXn{cwL6aN z=-x4F>D9%O43_m4K?WZSB-!gYwLO(P=I?ISJ1Si?6t_KI9kZXW-sN!Y*tp|~9Ut#t zqMI+0yu4AVlhTKNh&Pj-?yQO|2>N(T7tizr!P^(w}E+v~(h+g;iQm*aHfq-`*5kM%7KVLc|h0sYLMqyJCaZ=^-P z86-^V6JL4M~Pv2~d%rt`uq@#+fU zD&8;cnpHv5JpNhbPP;Oij#91=Xp7+=Ai0^LX&#Gr#ClMGU!2ItIR4j3`0K{t|Hp_t zT0Qh`gx1iFsG;|T?22rUA>g#fV`ZqjKM5ug6C>*F_YqSC^eJmRwg7j!1Vi~0#Mx*@ zTrXlGMAAhxh@fwc$J#E^GUuR7Hl)SV-$oMMP6zFOE22x0`mK;4KE-%U{x2hx-YDHs zdZTnm>5bCmlh8qE`@bGxN}D77!M2tMPpSx0tI8vt&xd7!&Q}I~d zU?s0AP_59H+1h=z`kzC+RHSNf~kVZE_<3vnw3M0H76{6OSk`F*rXVHx4%xjuJd>M ze<_Ag6%+Ot$N!Tg{3qdyN9=#={Pz#nNTdI!VyMx-TfCb5jsANhls+CqO_DIN>Q07- z`(@EqVvR)v=;sXDV}F@st3fzG!(Q;Iz6BVZuKWPm9Y3_AF#=(?SlBQ;Tc^f!4 zm6HXFT4lLYIwyI8q-_oCykXiid}BSy?+;}asJgNoitq}{PuI)cOUbWSzSCUiigkX7 z7UW}K@FXzMpjkZ#42xPYEEGNs%dt{Otg`ri?dt7NlLNl*BSo$LmPE4KC(kG#c4%pz z#rL^nLcUK{)#xJaR^o4s!ak@VQ%HQ8mi&%mYLlD^OPQysMt43gpS{ne5=yI18;dbe z%tmjxC_5Q#EhcdrjV-?z!+Qkr9{!fepDzs?F=U-e7PJK3fjNCZdt7oEyp-&@gJVE=qokw>5WI^}NW8v%2 zm#4A&5IYiHQNG8)?F=s5yK`klQjrejOul$1{Mp^h(XP8aLEGA$KbhaKvwP>y!@OrN zp4+vcap&$ZH(cVq;ivD>C~vvPEH`om_n28D_xasM?z4LqmLEi^C+^<6^Ygpk+*xps z?2cRR;qEwb&)z#ezvs<6(u(4-+v6L;#lxkn4bL17U58&T#xEBP$Co0Gu^|66&q=(= z5xfT%&K|x;zXnw?VUKb0KUu;*8DBgi|E~<&hHHb^yLZkBe}4D6%(_wsz}!;Bm$V{#b6NuD;5Dn7frduRSVraO|tT91H~An-0J?gs6c?ofmk!_UyVm-Ih> zE0x;>C)M{{xJJ>gP}|dWh;PKX7UzvPseUNgBoav`Q9d(mKby}AlC2`Gd;uZoqpld7 z9oQMD=kt1tye|hoO-F{V_T^Ol#?@3>>H3Z9oAxJsUq^BYDUb{A`cAlXf+G6P5L>m-*XYu= zbG*fSgV)JRzbVCH5so)_iNnRO^8Rc1cjROenM|^XHse>WRAGT{A``$k{8J_TQ^(+c zkk7AjEt%Q%j_$~ z*2{V2;RR$CST`PfB#uE^aqO1$y1cy+hu7=slcwt+lUAimACxB~hBTR#x-x9ouyp#$YI2bJNx4CaI#8bdtR9aqnO(e-_ZJ$YH- zpC;iC-Wuh9KfLDjE&EQgPbVy853>%BmyW}-(UJjmIS{Gd>t!?E@Uk1Zjdr3hwxRCM z#^opVt+QeO2|_+lpN@}2pN_pm?QS%r`JDFaoWWwdQ@Qj>e)D3Jw~fd>M|khzQZF4> zkhd{TxxhGylW{`FckFcv_7r8^rn-#^TRipwN}}%*bo3|;KAFOtnBcB+sX_@JnUnSm zX_zFzBgmt6Ysyn?vZ!oO?NPXu!4p7iFjw>qW*Yi~erlI@Hw`oL_yT z5AJwL2CEt=7!Y3n=Enc>mlZNZ42dHx-rzU-{Zd+QixNCG8MJXB{;@h{_}6+B`s<9N z&Y9p+gnF72xP^YIxA(T%MDBYfPPKa_VGgB1631hw$~g5~}ah-hQ8;u8+qK#o-s`)(1HF`K0>O3)n6Q zI#@7hU-UE4*0=}0Op2un52?#?PHd%QE zMr#cN`|wWt!M{gv)wgI&30YeAeuLJ1(53D?tx}$;)Z^JiRk?u+HtSoJ9;N%|Hl_G% ze?Qa`GDJJM;dnfZ=kFa;j(ombtw~UaDijroN>2heQL(4rH4guD3IBB58|D9?(v!+@ zL4B(Na`XY4Laf8ie#p=`>5a$r2Z?^oaQ#tfEpzLA1}B$UU)t78X@i`DWhh?(Z_PGX z`WluSVGUy3K}TqLe}vxCw`fV0L*|6{xYMbL$Nm8c&koa4+Zxac=*ACU%tQi-^Rpc< z>BdML%BTN`YdrPv>v{^3q?qK1?fZO;;K=c|it;ZY_4o8sy8Ast^j5s<$>Db$u@f(W z-_U`RyLsl&Wcmva?09MWm|wW<^6LK^B>ZWsH-bNz`sk0y3}??BX_IVn89$sJ4L^{^g^Jde`f`wgMJoQUvg%r111@xM{R-#7;U^zQlPiW9&w$Tl`se^|{ls%qzDP(AMtLoWwktIqN4AhpI?(qvf4 zr@oW==3|k08S&VoLk#hgAh6Hyv>D_;{@J0Ph~l{n&_xF5f~{*ZHo-UKAa5eB#j@Ii zmU!&QP#`F4)Mu;!_79`O7xP99I3(G3s=EHNhxO| z`lOWCx1mq^Pqgb)c2K@2kH_N{9em1LG@#Z<6pgjlo~Pf5%J9#z@3#!1c3i2}AOAJ*z-fO{BzaC1=2|#hzk!NqI?y3_b@|@`$S?uU%}J&C>?q#;v-H_scD!> z-?+#SM}`6S&R#qpA$cjG%L;$9gui(V{v>yj5O?gYHhA_1r&J^aj#VeQr+A(;Z7JOh zu1xTEMYPMlj-*yt!Sx?qY>y;+H10z-P5A+6X@BgMExT)&Cr5aZ@@3&6qW2B+Aj?X; zM|qa=C*{GZ;8id3^tLc|mMiphfUa^oK`(nrLB4=idf$$hrd4P#ZXoqM9M|MjD9N4X zJ4>rMP2x4p_KE{2{`8tdE~7wfvb>^BvqzHspeInq30H=|%=$WF^qAbHax*B(-)r)u zyVJ`}2yg36ao73`6`|z@I!9mvALHb|MZzCeywUc5Pj!Z$a%%z|<@fG`Hs9N-LTz4e zc!@k}j8FS*wcgzv{6{3oqjIatlTd%|UWzf5!xT%3yAN1iIgIx%;C&GBJ-AZYJc9cX zq}3sfg)Ly?;sb<%p6Eosg8GTaV@C%WQL}^6>2Orj_o1^y=$*RZJ0FX8h-35U-NdDu zaH1#CJ*nITyKfv8sCcXscAW&z5tpXjfqv_E0#oosh^BWT{p*O>-%Ei{0p2Bw_x%H| z6Bz#S`hSLmf5sU6={yVBC?>S#FX6&iKI;I{E-z{&ay3Wid}%#KA2@mMj+eAPl0PWg4HWJaAN>W|yW9&(bG{DH zZLlGTHs(uy{y#rSWr56Ya@O*{@zkOvS{PWhM1@vlC12-k;E#LUyvdt$?rG0)_wn*t zpdXCiUA2=m8GFb+pOuwUt{9+Vwo8)S{tQ7n~PFHkEh$t~-C()36v zIn4YD^>u`K&r;) z6rqwg`6_*Et*q|Z$nC(^KEO7~j+hNgOGzDb`1y#uLGjoIp5u+dDZ$6WcvE}RhK0Kq zR7q5rjMhY=Le_TohEiS|yf3(*>h;S@|I3o_M_*`E{$Jw8pN8iT*VQGCeR)4=X6!3n zzZ2`~QeFi)#aqJiI%03!K=LQ`09T+U(ZS}$^}a6XkXMmx(=STN^v}EH4L9(l=?K4= zPYe>rCmZZRGCiedGry+lRMgtO!c_f~Pl42nE&_;kan{pMy; zaG@ueL?d*z+6q1kv;G=~FatQcyFZ(%!V~rw*Z#xx27`1Ex|i^$2-!c#5{!_k>o%($ zLmS8p((}KczADxm>A55FY7Hr94XGXdKsvFfriyCqJ>*>j<=oAdGof27okA!=3O^K_ zud9C0qt|B=>y>_mb4@MLTaFO@K3s`Y;XHtLNV54P6K3jfoXSmfOy=GJ2L2oI*wz86 zBWzhb)$50Sb{<}~ceu}{BnS9|d=~k=Hy-vE-g!h(+v8;8G5!rwLq zf5{f}a$M@k^|m^xP6pg4M*0@avB1b5W>}W%VR4hg;&u~S+tU}@_}S3m+kmHh7}s8C zYYHkqFlvxDM%HfKa8p$^Ibb|ZZZnV>Y!8|Cj~h1zZIHZ>CexC{Q3Nuhut(XDw^7wl zweh(3bMGzt;g2L=s{^}+7+Y14_T@S*+QvzzC-&zD|2t@x${N-wKK9uHbnHM@@t`>R z)Z5Bnrr$v1N-O4~V>D2Sk>w1cK0#-;Vk{|&s+h3HIQ%h$NBQ55FCMY~J>N&AqBMV3 z+oNh?>Xc1HoYzZh{V+yJFhXb@h)ARK`!JfipPxd<&reta)CY$>%sfmU$|oGGI`3m- zP#OgW?SUS&Y21NE*NkrGu*`VVkV7%tv*V>)tiB+%#%uQfv{@t9==Qr(1Y&gzV=0X* zklbWp1W(m4o|t1^k;ffqUJIMhv1-QpNrYI7ASWokBnW%8*lV!IsRgSQI;%9&lmtr; z?OmEeN{l0YlK!OeC#An|v7NHZtN-ti@OO;Cf1iu#BH1JdE4PfHgoF>&f~b{6OZwFXuJ8QHD(jB|IUS~ zn6Sq<{?C^1&&C&z@PE<=I>Gw-G}P}A*YwUD)bDXuLHiL`4(hi(@`5YcWfKw4C6uEE z;rE~Up;^qW=roGCS>z@mD^}ttjZYTu*g8|iJ6X76L^;0!k3NI*c!aS&714=#G@V*8 zj|zDxmPc^E5obN}JcR3EoV5ragnfz91*H*jY!P97OM=)g$pvk>7G48q2k3l*Xk1QY zHWJnK%F6W)nVr7x`HNrRdD>P{`qH|?AOk%X3{0HXu< zlr}(?%0mZTY!}tNI?|nJ4Z7naZ2{^Xwf+vIv4kb!;6|Eu_CKV(Hkziw$e&i2g7rWe zoeGc1tAL$4nKW?pJ<}cEMCiH@Xu+N8HsXXndCSKt<_z^6PVl^GNN}6HOSbLZ_AK*- z;aOH`rSeTxL87w;82BKN==1>s%K05U2PW<@4*#hV{!_=`|0(#Rs0cP%86nqVVgS|4 zsfQfK=i(o4+u)qzWIZ1nGQ8H!{??AbFfQU?kBm`k_}$WtaXFzdr;NFxpJ?EsFUS z46~q}XgTVk!`ieQ1ML%jondws3Bzv0-Hv%l&w@foo<`8xm0}-&bFC?rdz|ErmHsk1 z-{c!AGlGM!7lG&L_h8l{e*2kT$Sy$->!3xLVyE6IlQ7S5;-dc__m{)_neZGOi(=cJNUr_%j|0kc6pj?FU1u`T}Abo)7p@501M>4DNsZ_8iB zF*PM>I-S(>i%GWONs=e_;x_x#jdk$p-{dXH>S=D|nSvQzbRJNfqlx+0VDef$%_Ovj zO`E-X&r8I` zf4#iz)bB7B1$}?|UqUkbNcqQnJ*vU8oe`U_$|-XsI8(jHe2o1#e2e;pE=ng8_!!6k zxf1@6f1~X`2@>baXa}YF7L$OVfv6iuG^1Ze)QxE@IZVAcX}|ozIMoF8=tBjww-9nT z9@{JWP=N--c_RKYshqd4Ruh#W>1ZdefR&}UFUZgJ^c&`S8eQ`{88Qzt|mjl}%<@21o7qb9-{ZwTlIv$hA|!wbGg5 zGm=A`HnT4@)qB+IfSj@wo4j^vZ|?pCevBrs)B_QMvoFJAcALwM2!GmV z_F%2E34DyhKTpCxZw&s1atrDr+-LMmc26#MAl%(&_bqayI$v$T9w zzAx?VY^w8w>*Y{KB+>o~Vr&=kp`p5tg+=Cr`) z`x$bj(dvuC|J3Tme9{p0E&o?!#0MO!KzfdgQsNhA6S|>2D4-Lto=WP`*e&`lIq}XD z_!x(OzJz~1zIjCdCtlV`PWGeil@lhcey_d^);Cvw2i4V{xfgc2GTJUvUF}OzFY_Z! z0dG4K+63vp$tU;Ji8>j@|Mfo)3{e^w+I1zNdYRnQb1T;C3&}9=qn$O0a|H=WUcS_t zyqx-QNm^YG*TYb)4m5>l_Qspn-E6wKd#iMh`V0@np1w$FMCaCd+D*0k zG*XmLx{5G!O;fIhhWFRFJgjyrJ&%)jmNTND8YF=7-=7PDq`UqCraD5Pgo=ln7rj zL}wpJ>xI#_?8=kGS%j0!^sOa9)=3`xS(zxCAgi2fcdE#(AFxch+=jWTWy{eoL{&`K zV;ug468?qw;t~9RjW!|0UAMhSJ-Pkh*3edxrx4;1`nO`ce!IU+-V(HFly9W3a6$7+!>*Qg`|8_&w0+8U+80dQx_ax6Zk}>;v=v1Q$zpk_?fR`N{zG|m-Bs9* zQPL}7Np1YtvOf8@vJUfZckT9C^(LNa&kfVO{I#=h|MBg2ZJ)cHM6JDtd6#?S*Ehiv z=`erwX#VTO{0qk9UyA&5e~^E|f{zayhyQd5|LJ4!Ck5F($pXo{x|_6|=A5wPFtbQ> zUhhb_hg(V0O0a@w{^ucJ7orqGtXQX2#e~|pQZ=-_=+Zd&(SbZIBv0Ej`b;!!v>ehp zuEeOKcQhyZbB+)xFofV2CY<6Em(AOH_f~W-9TF|xy?x&Hn|ZWFpdD2?gVjmpm=7-f zCbcDikKso^X%B5j97YM@^>C^#7o|mn;ET!2=A$%PQqhZY-96jqZ#T;w+(I$!vci9c zg#U~&_|sZxDd%sG?5%+6VM@7A29S8ko8t811}ymI?iZm#Yw*=zhx?xTcCd7J5# z(p23}uE%iN^cOX~_tN6J=AB$tl9?H~mz7{v(D2U^|BJnvqj@ybvwK!*cW^<|T{2!v zQ>Hj7&3|)#9AycEMF*a8wi!tWGwTVLvYj|Y=`>6uDQ2eVn~r}) zE-U%(v@yaH+Z!`5se4X#$~jnR*37`cRiau`Wpg z8+roTH)h}z(Yj4B_qQ_(dyz)Qq{Sl97Wm`}!LaT{l4H@zLuPi$q!Nx&jU@pwW=axb zpq(R#HIau9qZIT(0voC|RF*PxEaYLsmQrRm4?5ySI+CaeV?CpE^lk)TvwLA3at= z^oAF)9?@_L`RHk+9Of}H0c}FlzKJ)ynB?iXyypMTlJK8327jr(bRTV;OY}PTB~5G- zEcjo>)uXynwAJvU4D)uR{OXSsb1=U$Q`Db@c*}C9%emaS*6G2CSywHSmn|h*5@o_E zmpr7CrFCLWkVK7Q{b-s+rtKtlbD~+nO!YW5XFN{X*$1F!>~Y0gG4=qgt1!f&S;kd(am*`wKOQV4bSwek({YSnT1hK|lg3~9xA^m7UdHNFxJimZ-nkC{ z8N3f>Dd9wU6ZjaX{}oI47vq~p^nd7BxeD~Wf1h6Nbej5w)2dYJ_2H{8)16vudBUkz zq$J2HG-i#$$R%_7*4!*&&5~1_z*4d)G3aQAFHpOwQgL6t{=-KXq;|iTu8~!!jnEoH zSWB3hJ-OJLujm%A`mPsGV+^iU&C}4^No@vZo*q;s$9SMbRoNdU&%+zSOi%6hsSZnk zDNMoY4JjYkJT`aT78`nyRRcD<;!vxlBEwxzb}y&16nne=iP_`*5i{;@98eGwt~U&@ z^JIZ0va3MzVM=+>4x3l>^Z<)B1=zyw5B?fS@ua#_%S&t%82<74KO`XRA6UKn68=o| z{9ua5w7REr_ZT9tG5fVzw&)|QBXqiXk6PZr?sx7day>`F#K*dq zFP9xufzRo0B)up*r8^aEVX`l%HbbUFTXc!Fdu0l;_oWm&1l{50X ztyeq6cOn9l{k1cVGm%%F7Oo@xnaDG|b*8%8I#*6!bS9D(XRmX9X)gEc$dxm7-PT1; zF`Yiu>y&e9^1M^W{XQK&E5ry}7rYE6@G(yQqs^xLKW7a75R1DgZ%Lf>sdkHEOCl@W zWcflX2j4R`je_#hle{JI=FCNk9&_Y}IWQt)7IWO?+_L!L7xy(k)O-i0(%r{R)@{ek zY#%q*z;-ZJB41=ElF!i*_+9Pe_cu2-FXwc+OI!k&~IZvlPVoIXEjW19i3hJLOPm4cZ!|93f}Al1%-co3S-hbNkl6BzhbZ}#V?nbT_we}?C9(#)Xb%R~*(5*DwQ_38n^|x|T z7o+7CGe=Fko14L9vb1%u!0hCHj-K~l)7y^N<)o2mN+hefEoP3pVER?_ z&zgUXx4Vy%4Zqz>qiv#Q(3VRPg`m&qnzn4+q8K00^J#1zN%YJ4ml4x zSGuglXPh?Z_?=uA{#%PxXLS5wF+I6da9WY3Ca2S_#j-Oruf7z$0JO&XRu1o{O?)qD zRVL(&Y2_F#ZzZ%g?BWD1F9llO7P3*&@)o4X7>ohZejJrGD>#ljZFrDR*6nTfaC+E! zH-eUfXlpic7P%R%kN(u!(o}hhuFbHbbmQX7uKzb*!k<$8NdJ%avoZtu=rJwE=pQp9 zSzu;pK0J|W7W+=l53`5+PA7UD$dlh|7kgh><=2Tls-wMG=r3e}vn-+ifnSbke+I%+ zLK#SBg5H=xINkcvwVnUN0s<5H_V(ht#hNT*J19@<`N0)}t<-eUFho$d~BY{Q+yeyn&V+%YTu!7Hs&4>w6gF#!hi$ z5QAMSzWDhT^+QB|5#O)=75XUXt6Yhz*e^j{Ow?nX{cw~~9dBe{Uoe1Y zX`F%0ZX`UimKfcg537a?3BC&0A@r>fSyokeFZ{wP$#RR@s}Hmpisos3sez(}JF@8UG-g<>skmC78n3L1M>l>4sQXc)S%5sJj=!BR`dv+O5X0jpO6<1)UAG44R7fqYF znwfpr#k6O^w>(C&X$j`}Qv2^}tfM%x z!pEjerZ8PddQ3kvK}aLCHakPj%DG}-K}?{uVC^{2B|G5jW|;nM8yI;_@oKd-LU)LBmG-$gsbXFg(U zg4b7}ETk9L0XoQ2j}V^Ebh>zx%YZdr?E;ya7RH=DrTJ{B?X=-^vv&UJ=DRG~6Ai1W zPwP;uCH{;{-;p(QQs>p!|4>Rh5r3Cf!-PG?;eVBc|5fg_l3QQ!0?08L%iO)?LGE zAiZtju`!c~uj6c~z1`5>tXAaR# zV2;A2yVdayv=%-1-tOjbWTU-1a(u~z1s@+a4*#nq{I4E^|3A8}acW`hvAh! z9Hz7|4YV*7w4iB{)rr*b&?Hd9Dqh!^(4fWIi%mo6|3_*#bTY^v;3+kjj+h^zb48?i zV=wmsF5&Aiohx$IBc(kpru|(^3k|0o5Yzq^rhXn$uArFmSy*{k^(!g^G8;|K#9I7^ zCaH8->Xh}W20CU)rH2e_7qA=&eH2zFIu*{u)@xtHDx|7Mi)ASZndEm^^^UGRd0FAV zNWy>782tD4z2bhw6OZYKub=d)4{8n(`)@sT{$D(HruQZHOSn(qv5k7I*EC=*Zf`uc zt8Xt_h%J3|7SMCmP8}-={wKtozyFGS2tXTy&+*rF~Dkk0Vc)I3G{? zg=6AfT6#S8=Uys5)J;#g4`cQ&#-{A*Lz-H%LQ_W^>bhdG5Gzm7x2o-(bTH`3_KQorhlG!H8qRfvZ$n8>itUSU$vv@PF zs?%a@uZfpW&1^C&a?jGYF_L61Nw~p%gNO1v<{{u+?uJEi-EjOi#CHgmrphJC57+Tc zCT5|fP8L>7{SYNt>W1Y8msv}u!e?y)ALHbIsf2&&82p>~jHX9jH?==zW*t`$&o0SXjJ^bmF~VHZR<@4sNN+c+b1EE)I_7HmL&PDg)0VJkZF-WJ zI#Roew<9N=QF!Y`E!K}Aq0JE%{SMuu8IP~H>|L~x+rD`M!#}?LcS-oW#^C=yKW|@DeXK^a ztWN7BB)n+K>XuiZzlC*vHNStzHRIDo)UJlk9`0r1G&rYome0@6H7yG6n^78%#pB8& zDzxl*BxohdeOTEmfhcQLb$3Q?%6Twy9Z;?id)jtVXFh>Gpq?n;SvXc>{MAWHQFVL+ zZ+$GYF{_D=%MHc;2Q$lOg{XdT%9B~21(M+ zwg>C8sv4ANtN%KnJ~wh#j=PWj;O?IW`ogG3)ugkH*ay=87{#CD&JdWnMX00xAzDXNQyPl3W1c@|<(Cg53O^<}>hP_WN+`#jg35RLgjC`y3gbz=oF=OV0~f$o2RuXH8UeH)<`qzazK*IANl z*Ny&C0)FZJkW_-jhkb;4r04$_q2+h>$w?c0lJPv|A5Y|C9R6hz{up{0#lM12;8s|Y z53LB?n#TwYd{!7U=TWOh)G7;#NDl`cm-6_;Jc-D2DTfj{Dusq=3Gl>PA+NR za9&1o7IrtgFhg_A`8pC5_m!jbf2HSX{!c|5F2(;A3FMNl((_e#{_68Ase+}CuGX-_ zo$AtuOiFTAcb@8RMdu}iGl4J>es_*)izVctbK7%Wf}(RxZmF;-R~BNYD#L-?s|3Bn zjTPh%@!F|ta_0$Exe8%Rt_(BEFDv}ZCH%|B;P2tm&!27lV$R@80gyOY>ja*!EQ*Qvnp?TCa=iciAk65{`k z%%ec&`G`rW7)F#h+BZBj`t+g5+Zc1#J%YVNaF!k<;p&WjX6kiJbJ#t6i#1Y|hO|TTGN>rfs3BCO1c>Uik z;qM-Uzq;5`>*nRP35~K^_Au=~{jlEi21ht9{D~DZ)C3v&QRHUy4$5-nA=v2{DQ$_E zA`6Y=xm3gdZ@=*AC{I843G28Pqk3+P2xY=!SCa4BEC+c*U__H6Gtxqm1X#j9{K;x) zcq>E;gM;Lg^_GN&2G_TplgT;kEtpTfkGObc`1;X2g`}9=WJzc`hCB!Po(NN-oyhDz z9)FL7zh?~o#sJAwIk-#AFANBF13)4#R41n zg|NWY^HaliviQ@-MJ>CTpG1`KuBha(Cm_5leu#*jEagkc^?W0L2)T1$xu?%7!x@B` zd!2|-TbZ2{K|6ZP;~FKCwu7j9UR*ip%^%cR^#@I)^8;h1nf`4{YbzkaB>%YL(f%Pd8sZW1?f}rxKmqKr-D*q_+K*ye|?Lz#wM)&uU(AYKO|fF!&PAD8Za;8#GY+LO_B+n z8HRaWSo2GT)xM?>?kDK`3+Mw{bzvD+=gTGaWz)k8f1(dB^i3{jZS`ed3xmBj)4ECF zHy;)lnT6&+;=*;VRi!=6`8R0uqnP#oW$36Y(!J1UGvylU%g#qmx@3sW5@TeB^22A1 z%vA-$Pc8g$Z|lqG|BHTR=p9ZGbRLUO5zZs>(^hDnFT}H}Xn&Nh8`US!nQWvW3OV*o z=nIVg6nOohxiIMGpEzk56Zsg&|JO?RUpoeW9ZB#73lylYbr!q#f+45c5AR$$$MU;~ zzU5-<@c|Xq|4cD>O0kMhkD{E74h_L04}C3}hw>s_8-}D1D-1Tf=sH1c^flavKOoXZ ze7xvhkBQis!Gdzn#&`3#Sg?WwX5tx^8G)TDT!dZQlP@aBw~;B!zKc-&(ueU&74c(4 z{PM95nU%a;My*Z%6mH?WGPI5ato2RPE}&OI3KXzZURnz_9{X+l`^bFIJjFW?SBkmT zo$6!5ChTQOSGLv&mlgiYB>b0+!9Ufv*A>H>9;%S6D%Fdzw~cHV7BoQ*xl8Mft^p1k zN|>S|7dDGrRbLj1P>iMfB+5a)>RTx{vC(G-v5pj0?4+w~#$)%zv2L*F0~&2PhP`_y zA?-R?@0sUlTdXNB!SDpZm4bbnafpQZ@k zYl#`D44CJiMxJxYyA#pY@2P56etnj%7ftfDX#G=s%Wa1?FTcewpK+?NhUBKj$zC#( z5$2KJR@tHf$Q)YcQ=rY+dg|jX$#|tecb6e}QjRid8`Z|sJT&h!xU%p#ud6bv2G97* zXqvwTD*^g4Xjf>$9^>Ty3JL!e_~H@!KXz0W(z5BwsAf^tvVy;La`yL;bolwFdyL-n z3M(0m&A{Fa^lBJLLaycZ#?5PQF)YwJlZRJjzYvk|3&Vr&kV#wdh+Vpsf zioP!+toR#?v2(pd4}SQP>swSFb715=XJZ`1Uco9bZ$S{(t`m+D{d+jy#);MCoOC|` zGJ%hA_^*`kUx{xX!G9O_X8ogP+R_c=PUD@-lg6WdpUYS3^Aet8gA9Zoe)=%f!U zDp>?O&Kvi!wC7Nv^}XTkFeG?Y*fpHR?1W0_6m;E3W%)`_&=-R!D+9Vpl76`ly1(Ty z)W-xq#^Jw8!hh8m{GUNR^tSBEQ8?ey+=3mnDw#=3Es)XGXoX3rw^i%8jXZ0-jk&>? z_W;$e{}})Lz9!{Ad?@?ou(L?LO^&@@;ZF*TEDxi925jiN(Yy-k#bFCtF1_9YlX%B(FN8_8hbaZ3ozf<#gTP?k#lZ5{3J$xU+FQb_>cUSbLH__jhp| zapDrdIQ*}Z@W1Zg@Mp0$=SsGFi@!{&8>|VLIW0t@f9jobjm~l6=E)UtJH_g3Us|~n zNDRd?2Z-Hi11H5cVih*0h|T&q8~ytL<0#mI@g2qITMjxj?$4G}nt4BtwN|jJpvt|) zrV??>9i(f3u8}+_n9DszKgp8|GWS6TMKZM9Uv?APY>Hb#pi;?(JOKsP zc=MOtA7HR9d_QQ9mZxudCs3=ZO=6%&><=W=RMco#BS&}oj>i)54s`vILG)_a=wa*_ z07}K0rxW=YhyQ8`|J7sgr+s=@!7;ZY9&?CwMEPqs`0D_WJ2OCW=^W*+T^KD^I}@-M z_q!UVV3NSvRi4JJr@`k=gOgE!&*{3{Sg$?07d6bl^M_(4@%%2k9?xpA%8$Nv(kMp{&MThK=xDr->kCm% z$9kaUL$MY8N{`0A?q;zM8YWH>mG%dqa*5IiXdAld6ted|Qji|{?>(0l{?|+RUq1$a z;`n$EQ*!LioQ+oW-g^6~{Z%u{cQoxSq$%L(>$nU0S2SthLWHKu24W(%(4?XN!;X@z z4u5Q_WK!JN9n43qK+Nn5&3fo#$6Tt8e|1fQ-nfZrGLV9=Li(_7>2;7JbY4zhpEW~| z?Rth{-}YHaT9H)@f7$1!Z3X1P7I6LgDa86ww55z%zwLi0CY@pZxgrxTX1XWk2SX@Gxi0ZG-oyg>-Pjc z#_|7;B>aDbZywSA^i!YbY@E^mY;wMHC3Cvjn*BJZeLk{AZ1X|C%xQi*+!}!#eVAV1FQ~pscfzn6CHBX3iIOi`0?8Np}tnP7kw~u}N(+Y-XWwD9{1g8M(V>Ff%+5OGa5= zgANQ~N6kjDZ61g%L|VEa3rMBhKBz_cEhr&rG;PZuMHYOGwwqY{ab7~Ujb92GL9t{# zYXV$meQ8^>zl`F|dJF-lsx67?G?MJlf|e>(A0&|iWw#1DKN}pWPC8S3;vVDpf31Z7 z+A;W3+r1TQFTidSh{rZu?1e5x>+4DMr^U2+BemMb(=@uzUlKHff5nxVr6DqNk>J2u z08}SD+-swINc(Nq!+)KG|2ll}i2T=hG6D|- z{C3Qs6DXG{Jq-aXIDHzt1Aw}#38RB-XB{!b!|b!6vB zV(kNOL#u#ZzRRk1N;+au5!DgpMKbI$N1`u^V+-^>D7GW-Vk{?tT{(%)zi13gx04IT zCyhn3vuiHE$CQkgp1f2{pY{s>_~NB}b`3M_6n`^+C3S_Lu*W$3D<%A?*W(EO(=aYS ztvVj-8zi}ZN8kB2Sa6z*MJ1*h2EJ9MYEzAD7QJ?Zw?=4S7rehtYvi7l@VKuu6qOii z+C)i9aq+jnZWq{=bFeXqQBnD?jfX_I>JFc&-Vu{h5lj2D>Or z92X1ESs`Cq`yYp!XFT>_15db91FS89OZ79>%yW-=Dg*3sWievII>(lN#+q{OzXSES z^MY%t@DQ$~ATe~mc1Ha>S*}* zAk*}!|6F7s_I@t|jpWN-RKj`Ltl%kG@H;5FqL8K^q`wgQMMM95;B?{UDNkxPMVMoA=R*MMx!kYuyg$$nF z6(-Sye#8nA#g>ain^E>1VM>R)!;}u)pu?UBd!jLeU__89u}xU)>4%NOe}jbohB5e4 zt^Nhn(#Ei@hPJwY(fIAK(FnJMuXH=HR&uQ|ssD^@4lu|kgO!LBlw)K*9{cqWt$F<@@^|5P zbHYah=L0D$%V8zK0;53l-7rLE%o;6SJ4ogDV|@nC9DCFH9#tnacr7#v`sOZ_AP-B= zT!8kVz?d> z{Qn8s^{1c@m%=tg+wZYDauM25?D-0QQ->5Mi&~jytmq@08nD;g;A!;V0Kb4ubU)|a zJ6=kK-Y)Iu{P;%ZX)BO%T-;Q3><2q@liUvk=0s&cU2v|miCinTEX)bo>!=&(k3Aj# zYs3wWpA?)H${|M>tn8HPPeuHlP$?t`lPziu{@M11^^bE6*EOt{TX*Bi6pzHk7|LUx zj>m@38vG_i-3=ATRzWpP@24#wBTu{~%~cSVj=F;BT_WKAS+ zw5%pF5{918yCXv7?bZmDw?_2o{t-zAu01Mp=B7vzbVC9?Egs7m+z`PozGC`X?Ae?- zczuKwXLqlR$m;9=pZ2~4zNsqh``jdL(*+tRXu1Hm4PY0R(t-jy5R!6fn~qjme1YQR zrfKR8bO~(=scQ?U1#v8kGo^)*?R7w0u!`fTjQvJu#&srPdEeIROh6?nqt<0?3x&M@ zbMH+F#BpZc`F_9e`?Um~KIfi$?)Lo8`kZr~XVEP_cn%ZK=KB=bje6ibC+czNSk8_7 zO2>G?c@^_Sy0>q`8ldh5c#R?O652DMIZBeDD0 z-;TL#*RaChBH?cd!oQ@QWT?AI@~;eW?wEXPM+f#j2KtWv$`FZllsg3dl*rET+!V~i zKQYjdeAd^HjG8m$Gt{1c_3egUmIq1eKOzd$Sc`R`_h$n{w~zm@abIH^tabQ%+x=9R zOVb8C2vfc7I5O6xPMH92lNWmL8TbuW9-VL8SAR;>3PmR!L=MVG+R+}9zpZg^{fG5b z3yHN_*nLE%MY*XhfJbHT!pYD-!a9YYYNP*#x{N*t^TN4fpp&@Jn~yvo#lhc~h3bN& z^9)hRZ#%5;FO~2w4Z?r^AX=KER5nzvo8D7V0bM~d(460|!@4=8 zdqTca)VDKeS(A{K=A(%21XGJZcNE9x(Ht{s;F|p78~3!AXxrM4H*OIk|KO8)@LYcb&iQ+3JAc$Co%=PA{?(wpk4VJW-j2b3iE#c-d=>GU}2mdf9IoLl7x0m=^0k|TP6Ih zLHIM7mwH7`QKu5{-AZ?WL&tJ788Ssb5M%viE;=`N7;`D#pd9MliFrIx#BXIbY4usx zI7oa0qtDc9nauf9Q#z>*|3Yu`d1lsA?N*Y2RQjygyv5oPd5Q6D0>CsOOm#?ike;P>t2J{NbHGQ||kv}Qgw0vXd9?JJ}%rZ*nQ2*m@UsKP} znE$!|u(Ek1G1V(G@;%Y@?~{)b_P39oxxan8f$JpKk?CseF)(^&xKZ8&< z=G1kB*Ch~zUTK)?VbWlYXkyr@P5D+rb6DGo#|&rPbT;Z^-)F8>uJDX-cR22H!JC3z z$Yf%VH#z+~&wVC{V#xkN`hPYFe;d9y(ElqXqvL7WZpG?L;`ysL&Ly?lX;_i_XD>_6 z^*%kIAa$mC@+NWzYVs!OeEdab;vc;%G)MpCU4@xB(sjbyMk=IUJ=$qz;;Y`XuASUL zmz7M2f0ZdW`Lze?&!Qz)8nsdnjBRL#rpV1?jP1HBX^w-L-O$dYG;<2?jf8-c=2DJ6 zfBArsOGFDo*LTBXgM9W&-&`zHru~Z(7nq3~y-b?YtxOw(=dSlYAwDP0sLz0Jpow>R z#cyOd)IQP`FXIY__cIrLx1szC!M|L>zdQ(kloe)vZX&Sxynd=8%$S;_pNihGhz;zm zPE~Y`b3JUpdi`dKMF%8T?1{KtAePP3K}}T~v*q@0O(PxG)#! z&>V5Sgak=OeGrkw=%n*b+%rv1F-cAlj}+m9DXd7Ll@X4>eMV!q2Swh9_!#d&*NDNV z?+~BpPWmw^VhlKwfLHLi^4Zl5N-+VoE=Hsc( zJe75m)Iv`LvAP~Iq`FD^e2)=5sy3z}dm_ejm~;P`0WnHT-zswa2B`(H`3K z{nSr<4wm5x5ESR1%^HC z6MGuyDbN<37t!o5@*yo zn920~Oa0i_oczHWLOPiu$|8k>tWa^FHH--;D#8aRmy4cghf}b59r{i?Xt`xo3xBK z#)Yv5d{!2?!lv|j9(LV1a|*nZsp3D699H<-CH(C{_*<9s_%e?w$tG=cdn49NFDK0g zWjA)ww09&lU@Zk5530$NO|aj8+>@{oE%tc#W`pL~c0*+6=)`U9s~V%cG`^f{jNgd$ zJA1X;+Q0QBLeH|(aK0-|_^fHMYH>^v{4!kV-PlLhfl6yIH#8|WZfjq#v8jEFA-pp& z0qr}x&!ETp4b4${REj=H8}20KLt@8R%}sM2(=)9&7;CD5q`B)7VdB*|@QN zcT+l!TK#+@i_~audiwiPiQ_0;u`rT!U|brw!-)P#8=q>Y*S_o@R`{=!@Lw5(Kee;( ztM^A<T-|wRs1mu0PkLaw8y2ijYPl;#g)d!FxG`KpRJ=fbe;D*&tlyuedQmom})$)NI z92a{X;y7<6bbfT-2Hj^!dSd$M*pD4g^*xF_b;>|*7e;^d*m0x@d(Bi&jX*0!*P{Hv zPeTGX(ED4zNtfXmkz$ACNv6kw_Zn9CuafXz6@1O1qaFQShewBEU&lgWAbB61HJEF%x7L38E1TJ-}meK8-DTYk3UMPT(tDnv!iCjy`1OXE{*Fp9Hw^W;;?fl(QDzDH%aqm zjwCZ%wv2p?i8cO=AuY^~hWPpgie}}OQ8D9GBaCBIiWcP#>V-PsZ#FD%^FnVIwH2uf z^zf2Ytbc+EI>tZ3Vn73W0}Y%~$boGTX1kI!2wei5?|o^2>|}Q7NCvEsD@inbiw-ON zD&LO)YzY5C#($L({*^)azhoFIN@F%%yT?E9ji!m({55%InI=Fw ztsLEL{XX_yQYlUy==K#Y^m(W)c3K)InclNAz(i@p&OLIzH+mon)>f&nNRk%cJqc_3 zmhmQzxshqzW|-hf$%if2IipU*2@mN<3whioeTH$2aU4l9CGofO3~W5V$b*N6F#MtOe^nCxRYCaAEno!;cI-ZT zk>xGCzqj$CqE*>u&?~mWC-XJ|b1Q`xX^rgfbzW4phT)6~XTrAWS$}UDc5B-&OMkw1 z<9UCt`LdKKc^hh8NDeA_6X?8K26h;wDAOZ5;n#~KiTg-PgbA2gV6wjPF|aRClr+J8 zttr9br|U+tyGb1O23m8%u(MF{6dh-7x~R$3phq&$`@w~1aV$qmb%dCYHy|HJV8%Mq z6q&y(IWjj7J(w>pz5(269#}>D5k#pML_TzLKuGzomhi6*!XFJF3me*+Cb^tY-T7P* zAL1$Z&dJlH(0=bR~+v!}>r zo0IDumY zj+I+z&WyZBSNYLgV8=Tyzkm_H3poYr253%*9jwt(Niw0jW~^QFKkCCi1N1*gvZwF} z#=1in{qLFocS!g$Vp|F1N>UA{a z{?^waeC_KwLMBSRw|ssx{|+DHpl$pX9_tA_`@FXcEy5IxPSu_re7M)ccjAe}^C;q{ zKwr#E-ohO;jPTs(B~!+`uXB;q zzlB$Z7yhdy{8tCzPo}53=R;bPupgJ_k{t}zWN#2Q8|Jvf9l4yw;TARsCwMyFQpy(# zIsEOy6Nc;D|B1ho-ClkjUx*kgUAG52#2f){QNI%&Hq4Ok;1!yAEGuvu_(FjfuV>xA z!}Sx~8~D?FF=C$aE#

yZQh2y(^3%_w!^b;}-ZFp#bL&7*gCHHg4t? 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 Date: Sun, 14 Dec 2025 13:31:11 -0700 Subject: [PATCH 091/134] Touch up readme, fix build.py --- README.md | 43 +++++++++++++++++-- build.py | 1 - build.sh | 126 ------------------------------------------------------ 3 files changed, 40 insertions(+), 130 deletions(-) delete mode 100755 build.sh diff --git a/README.md b/README.md index fd3ad56..d3bed2e 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,35 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a - **Python bridge** (`switch_pico_bridge.controller_uart_bridge` / CLI `controller-uart-bridge`): reads SDL2 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux). - **Colour override** (`controller_color_config.h`): compile‑time RGB overrides for body/buttons/grips as seen by the Switch. +## Quick start +1. Flash the Pico with `firmware/switch-pico.uf2` (or build your own) using BOOTSEL drag-and-drop (see “Manual UF2 flashing” below). +2. Wire Pico UART1 to a USB↔UART adapter (GPIO4 TX, GPIO5 RX, GND) and plug that adapter into your host PC. +3. Enable `System Settings → Controllers and Sensors → Pro Controller Wired Communication` on the Switch. +4. Install the Python bridge (see “Python bridge”) and run `controller-uart-bridge --interactive`. +5. Connect the Pico to the Switch (dock USB-A or USB-C OTG); the Switch should see it as a wired Pro Controller. + ## Planned features - IMU support for motion controls (gyro/accelerometer passthrough for controllers that support it to the Switch). +## Limitations +- No motion controls/IMU passthrough yet (planned). +- No NFC/amiibo/IR support. +- Rumble is best-effort: it depends on the Switch sending rumble and SDL2 being able to drive haptics on your specific controller. +- Requires a host computer running the bridge; the Pico is not a Bluetooth/USB host for controllers. + ## Uses -- **Remote couch co-op**: This was my motivation for the project and my main use case. Let friends play “locally” over Parsec while you feed the Switch a low-latency capture stream (e.g., Magewell Pro Capture) and the bridge provides the controller input path. Simply have the controller_uart_bridge.py script running on the host machine and connect the Pico to the Switch. Then have your friends connect to the host machine via parsec and play as if they were sitting next to you. I use OBS to capture the Switch screen from the capture card and just display it on my monitor for my friends to see. Audio is captured via Voicemeeter Potato and a virtual audio cable. See [here](https://vb-audio.com/Voicemeeter/potato.htm) and [here](https://vb-audio.com/Cable/index.htm) for the ones I use. [Note: Example setup video coming soon] -- **Switch automation (Python)**: write scripts/bots that drive the Pico directly using `switch_pico_uart.py` / `switch_pico_bridge.switch_pico_uart` (press buttons, move sticks, read rumble). +- **Remote couch co-op**: friends connect via Parsec while the host streams the Switch via a low-latency capture device (e.g., Magewell Pro Capture) and runs the bridge (see setup below). +- **Switch automation (Python)**: write scripts/bots that drive the Pico directly using `switch_pico_bridge.switch_pico_uart` (see `examples/example_switch_macro.py`). - **Twitch chat plays**: translate chat messages into controller actions on the host, then forward them over UART to the Pico. +### Remote couch co-op setup (example) +1. Connect the Switch to a low-latency capture device on the host PC; view it in OBS (or your preferred viewer). +2. Run `controller-uart-bridge` on the host PC and connect the Pico to the Switch for input. +3. Have friends connect to the host PC using Parsec; they use their controllers on their end, which Parsec forwards to the host (SDL2 sees them). +4. Optional audio routing: Voicemeeter Potato + a virtual audio cable can help manage capture/voice/game audio mixing: + - Voicemeeter Potato: https://vb-audio.com/Voicemeeter/potato.htm + - VB-CABLE: https://vb-audio.com/Cable/index.htm + ## End-to-end data flow (input + rumble) ``` INPUT (buttons/sticks) @@ -52,8 +73,24 @@ RUMBLE (force feedback) 3. **Connect everything to the host and Switch** - Plug the USB-to-UART adapter into the computer that will run the Python bridge. Note the COM port (`Device Manager > Ports`) on Windows or `/dev/cu.*`/`/dev/ttyUSB*` path on macOS/Linux; pass it via `--map`/`--ports`. - Connect the Pico's micro USB port to the Nintendo Switch (via the dock's USB-A port, a USB-C OTG adapter, or a PC if you are only testing). The Pico enumerates as a Switch Pro Controller over USB. + - On the Switch, enable `System Settings → Controllers and Sensors → Pro Controller Wired Communication`. - Any SDL-compatible gamepads you want to use should also be plugged into (or paired with) the same host computer that runs the Python bridge; the bridge is the one reading them. +### Finding your USB↔UART adapter “description” (port filtering) +If you have multiple serial/COM devices, you can filter which ports the bridge will consider using the port **description** (or vendor/product text) shown by the OS. + +- **macOS/Linux (terminal)**: + - Quick list with descriptions: `python -m serial.tools.list_ports -v` + - Then run the bridge with a filter, for example: `controller-uart-bridge --interactive --include-port-desc CP210` +- **Windows**: + - Device Manager → **Ports (COM & LPT)** → open your adapter → copy the device name/vendor text. + - Then run: `controller-uart-bridge --interactive --include-port-desc "USB-SERIAL CH340"` + +Filters you can use: +- `--include-port-desc SUBSTR` (repeatable): only consider ports whose description contains the substring. +- `--ignore-port-desc SUBSTR` (repeatable): exclude ports whose description contains the substring. +- `--all-ports`: include non-USB serial devices in discovery (useful if your adapter isn’t tagged as USB by the OS). + 4. **Power-on order and sanity checks** - Power the Switch/dock so the Pico gets 5 V over USB; its USB stack must stay alive while the bridge streams data. - On the host computer, run `controller-uart-bridge --list-controllers` to make sure SDL sees your pads, then start the bridge with `--map`/`--ports` (or `--interactive`) referencing the adapter path you found earlier. @@ -192,7 +229,7 @@ with SwitchUARTClient("/dev/cu.usbserial-0001") as client: - You may need udev permissions for `/dev/ttyUSB*`/`/dev/ttyACM*` (add user to `dialout`/`uucp` or use `udev` rules). ## Troubleshooting -- **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, Switch sees a “Pro Controller”. +- **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, and `Pro Controller Wired Communication` is enabled on the Switch. - **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `switch_pico_bridge.controller_uart_bridge` if needed. - **Guide/Home triggers system menu (macOS)**: try different controller mode (XInput/DInput), disable Steam overlay/controller support, or connect wired. - **SDL can’t see controller**: load `switch_pico_bridge/controller_db/gamecontrollerdb.txt` (default), add your own mapping, or try a different mode on the pad (e.g., XInput). diff --git a/build.py b/build.py index 67a3500..dabefe8 100644 --- a/build.py +++ b/build.py @@ -115,7 +115,6 @@ def build(): str(SCRIPT_DIR), "-B", str(BUILD_DIR), - "-DSWITCH_PICO_AUTOTEST=OFF", "-DSWITCH_PICO_LOG=OFF", ] ) diff --git a/build.sh b/build.sh deleted file mode 100755 index 4371d2e..0000000 --- a/build.sh +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd -- "$(dirname "$0")" && pwd)" -cd "$SCRIPT_DIR" - -CONFIG_FILE="$SCRIPT_DIR/controller_color_config.h" - -usage() { - cat <<'EOF' -Usage: build.sh [--random-grip-color | --grip-color RRGGBB] - --random-grip-color Randomize both grip colors before building. - --grip-color RRGGBB Set both grip colors to the provided hex value. - --help Show this help message. - -Default behavior leaves controller_color_config.h unchanged. -EOF -} - -ensure_single_color_mode() { - local current_mode="$1" - local new_mode="$2" - if [[ "$current_mode" != "none" && "$current_mode" != "$new_mode" ]]; then - echo "Error: Choose either --random-grip-color or --grip-color, not both." >&2 - exit 1 - fi -} - -update_grip_colors() { - local rgb_hex="$1" - local r="${rgb_hex:0:2}" - local g="${rgb_hex:2:2}" - local b="${rgb_hex:4:2}" - - if [[ ! -f "$CONFIG_FILE" ]]; then - echo "Error: Cannot find $CONFIG_FILE" >&2 - exit 1 - fi - - python - "$CONFIG_FILE" "$r" "$g" "$b" <<'PY' -import re -import sys -path, r, g, b = sys.argv[1:5] - -try: - with open(path, "r", encoding="utf-8") as f: - text = f.read() -except OSError as exc: - sys.stderr.write(f"Error reading {path}: {exc}\n") - sys.exit(1) - -def replace(name, val, data): - pattern = rf"(?m)^(#define\s+{name}\s+)0x[0-9A-Fa-f]{{2}}" - updated, count = re.subn(pattern, rf"\g<1>0x{val.upper()}", data) - if count == 0: - sys.stderr.write(f"Error: Could not find {name} in {path}\n") - sys.exit(1) - return updated - -for macro, val in [ - ("SWITCH_COLOR_LEFT_GRIP_R", r), - ("SWITCH_COLOR_LEFT_GRIP_G", g), - ("SWITCH_COLOR_LEFT_GRIP_B", b), - ("SWITCH_COLOR_RIGHT_GRIP_R", r), - ("SWITCH_COLOR_RIGHT_GRIP_G", g), - ("SWITCH_COLOR_RIGHT_GRIP_B", b), -]: - text = replace(macro, val, text) - -try: - with open(path, "w", encoding="utf-8") as f: - f.write(text) -except OSError as exc: - sys.stderr.write(f"Error writing {path}: {exc}\n") - sys.exit(1) -PY -} - -GRIP_MODE="none" -CUSTOM_COLOR="" - -while [[ $# -gt 0 ]]; do - case "$1" in - --random-grip-color) - ensure_single_color_mode "$GRIP_MODE" "random" - GRIP_MODE="random" - shift - ;; - --grip-color) - ensure_single_color_mode "$GRIP_MODE" "custom" - GRIP_MODE="custom" - CUSTOM_COLOR="${2-}" - if [[ -z "$CUSTOM_COLOR" ]]; then - echo "Error: --grip-color requires a hex value like FF8800." >&2 - exit 1 - fi - shift 2 - ;; - -h|--help) - usage - exit 0 - ;; - *) - echo "Unknown option: $1" >&2 - usage - exit 1 - ;; - esac -done - -if [[ "$GRIP_MODE" == "random" ]]; then - CUSTOM_COLOR="$(printf "%02X%02X%02X" $((RANDOM % 256)) $((RANDOM % 256)) $((RANDOM % 256)))" -elif [[ "$GRIP_MODE" == "custom" ]]; then - if ! [[ "$CUSTOM_COLOR" =~ ^[0-9A-Fa-f]{6}$ ]]; then - echo "Error: Color must be a 6-digit hex value like FF8800." >&2 - exit 1 - fi -fi - -if [[ "$GRIP_MODE" != "none" ]]; then - update_grip_colors "$CUSTOM_COLOR" - echo "Grip color set to #$CUSTOM_COLOR in controller_color_config.h" -fi - -cmake -S . -B build -DSWITCH_PICO_AUTOTEST=OFF -DSWITCH_PICO_LOG=OFF && cmake --build build -/Users/joey/.pico-sdk/picotool/2.2.0-a4/picotool/picotool load /Users/joey/Projects/switch-pico/build/switch-pico.elf -fx From e549376ee471144216960d9f3f1bf4d25a40aa53 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 14 Dec 2025 13:31:11 -0700 Subject: [PATCH 092/134] Touch up readme, fix build.py --- README.md | 43 +++++++++++++++++-- build.py | 1 - build.sh | 126 ------------------------------------------------------ 3 files changed, 40 insertions(+), 130 deletions(-) delete mode 100755 build.sh diff --git a/README.md b/README.md index fd3ad56..d3bed2e 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,35 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a - **Python bridge** (`switch_pico_bridge.controller_uart_bridge` / CLI `controller-uart-bridge`): reads SDL2 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux). - **Colour override** (`controller_color_config.h`): compile‑time RGB overrides for body/buttons/grips as seen by the Switch. +## Quick start +1. Flash the Pico with `firmware/switch-pico.uf2` (or build your own) using BOOTSEL drag-and-drop (see “Manual UF2 flashing” below). +2. Wire Pico UART1 to a USB↔UART adapter (GPIO4 TX, GPIO5 RX, GND) and plug that adapter into your host PC. +3. Enable `System Settings → Controllers and Sensors → Pro Controller Wired Communication` on the Switch. +4. Install the Python bridge (see “Python bridge”) and run `controller-uart-bridge --interactive`. +5. Connect the Pico to the Switch (dock USB-A or USB-C OTG); the Switch should see it as a wired Pro Controller. + ## Planned features - IMU support for motion controls (gyro/accelerometer passthrough for controllers that support it to the Switch). +## Limitations +- No motion controls/IMU passthrough yet (planned). +- No NFC/amiibo/IR support. +- Rumble is best-effort: it depends on the Switch sending rumble and SDL2 being able to drive haptics on your specific controller. +- Requires a host computer running the bridge; the Pico is not a Bluetooth/USB host for controllers. + ## Uses -- **Remote couch co-op**: This was my motivation for the project and my main use case. Let friends play “locally” over Parsec while you feed the Switch a low-latency capture stream (e.g., Magewell Pro Capture) and the bridge provides the controller input path. Simply have the controller_uart_bridge.py script running on the host machine and connect the Pico to the Switch. Then have your friends connect to the host machine via parsec and play as if they were sitting next to you. I use OBS to capture the Switch screen from the capture card and just display it on my monitor for my friends to see. Audio is captured via Voicemeeter Potato and a virtual audio cable. See [here](https://vb-audio.com/Voicemeeter/potato.htm) and [here](https://vb-audio.com/Cable/index.htm) for the ones I use. [Note: Example setup video coming soon] -- **Switch automation (Python)**: write scripts/bots that drive the Pico directly using `switch_pico_uart.py` / `switch_pico_bridge.switch_pico_uart` (press buttons, move sticks, read rumble). +- **Remote couch co-op**: friends connect via Parsec while the host streams the Switch via a low-latency capture device (e.g., Magewell Pro Capture) and runs the bridge (see setup below). +- **Switch automation (Python)**: write scripts/bots that drive the Pico directly using `switch_pico_bridge.switch_pico_uart` (see `examples/example_switch_macro.py`). - **Twitch chat plays**: translate chat messages into controller actions on the host, then forward them over UART to the Pico. +### Remote couch co-op setup (example) +1. Connect the Switch to a low-latency capture device on the host PC; view it in OBS (or your preferred viewer). +2. Run `controller-uart-bridge` on the host PC and connect the Pico to the Switch for input. +3. Have friends connect to the host PC using Parsec; they use their controllers on their end, which Parsec forwards to the host (SDL2 sees them). +4. Optional audio routing: Voicemeeter Potato + a virtual audio cable can help manage capture/voice/game audio mixing: + - Voicemeeter Potato: https://vb-audio.com/Voicemeeter/potato.htm + - VB-CABLE: https://vb-audio.com/Cable/index.htm + ## End-to-end data flow (input + rumble) ``` INPUT (buttons/sticks) @@ -52,8 +73,24 @@ RUMBLE (force feedback) 3. **Connect everything to the host and Switch** - Plug the USB-to-UART adapter into the computer that will run the Python bridge. Note the COM port (`Device Manager > Ports`) on Windows or `/dev/cu.*`/`/dev/ttyUSB*` path on macOS/Linux; pass it via `--map`/`--ports`. - Connect the Pico's micro USB port to the Nintendo Switch (via the dock's USB-A port, a USB-C OTG adapter, or a PC if you are only testing). The Pico enumerates as a Switch Pro Controller over USB. + - On the Switch, enable `System Settings → Controllers and Sensors → Pro Controller Wired Communication`. - Any SDL-compatible gamepads you want to use should also be plugged into (or paired with) the same host computer that runs the Python bridge; the bridge is the one reading them. +### Finding your USB↔UART adapter “description” (port filtering) +If you have multiple serial/COM devices, you can filter which ports the bridge will consider using the port **description** (or vendor/product text) shown by the OS. + +- **macOS/Linux (terminal)**: + - Quick list with descriptions: `python -m serial.tools.list_ports -v` + - Then run the bridge with a filter, for example: `controller-uart-bridge --interactive --include-port-desc CP210` +- **Windows**: + - Device Manager → **Ports (COM & LPT)** → open your adapter → copy the device name/vendor text. + - Then run: `controller-uart-bridge --interactive --include-port-desc "USB-SERIAL CH340"` + +Filters you can use: +- `--include-port-desc SUBSTR` (repeatable): only consider ports whose description contains the substring. +- `--ignore-port-desc SUBSTR` (repeatable): exclude ports whose description contains the substring. +- `--all-ports`: include non-USB serial devices in discovery (useful if your adapter isn’t tagged as USB by the OS). + 4. **Power-on order and sanity checks** - Power the Switch/dock so the Pico gets 5 V over USB; its USB stack must stay alive while the bridge streams data. - On the host computer, run `controller-uart-bridge --list-controllers` to make sure SDL sees your pads, then start the bridge with `--map`/`--ports` (or `--interactive`) referencing the adapter path you found earlier. @@ -192,7 +229,7 @@ with SwitchUARTClient("/dev/cu.usbserial-0001") as client: - You may need udev permissions for `/dev/ttyUSB*`/`/dev/ttyACM*` (add user to `dialout`/`uucp` or use `udev` rules). ## Troubleshooting -- **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, Switch sees a “Pro Controller”. +- **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, and `Pro Controller Wired Communication` is enabled on the Switch. - **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `switch_pico_bridge.controller_uart_bridge` if needed. - **Guide/Home triggers system menu (macOS)**: try different controller mode (XInput/DInput), disable Steam overlay/controller support, or connect wired. - **SDL can’t see controller**: load `switch_pico_bridge/controller_db/gamecontrollerdb.txt` (default), add your own mapping, or try a different mode on the pad (e.g., XInput). diff --git a/build.py b/build.py index 67a3500..dabefe8 100644 --- a/build.py +++ b/build.py @@ -115,7 +115,6 @@ def build(): str(SCRIPT_DIR), "-B", str(BUILD_DIR), - "-DSWITCH_PICO_AUTOTEST=OFF", "-DSWITCH_PICO_LOG=OFF", ] ) diff --git a/build.sh b/build.sh deleted file mode 100755 index 4371d2e..0000000 --- a/build.sh +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd -- "$(dirname "$0")" && pwd)" -cd "$SCRIPT_DIR" - -CONFIG_FILE="$SCRIPT_DIR/controller_color_config.h" - -usage() { - cat <<'EOF' -Usage: build.sh [--random-grip-color | --grip-color RRGGBB] - --random-grip-color Randomize both grip colors before building. - --grip-color RRGGBB Set both grip colors to the provided hex value. - --help Show this help message. - -Default behavior leaves controller_color_config.h unchanged. -EOF -} - -ensure_single_color_mode() { - local current_mode="$1" - local new_mode="$2" - if [[ "$current_mode" != "none" && "$current_mode" != "$new_mode" ]]; then - echo "Error: Choose either --random-grip-color or --grip-color, not both." >&2 - exit 1 - fi -} - -update_grip_colors() { - local rgb_hex="$1" - local r="${rgb_hex:0:2}" - local g="${rgb_hex:2:2}" - local b="${rgb_hex:4:2}" - - if [[ ! -f "$CONFIG_FILE" ]]; then - echo "Error: Cannot find $CONFIG_FILE" >&2 - exit 1 - fi - - python - "$CONFIG_FILE" "$r" "$g" "$b" <<'PY' -import re -import sys -path, r, g, b = sys.argv[1:5] - -try: - with open(path, "r", encoding="utf-8") as f: - text = f.read() -except OSError as exc: - sys.stderr.write(f"Error reading {path}: {exc}\n") - sys.exit(1) - -def replace(name, val, data): - pattern = rf"(?m)^(#define\s+{name}\s+)0x[0-9A-Fa-f]{{2}}" - updated, count = re.subn(pattern, rf"\g<1>0x{val.upper()}", data) - if count == 0: - sys.stderr.write(f"Error: Could not find {name} in {path}\n") - sys.exit(1) - return updated - -for macro, val in [ - ("SWITCH_COLOR_LEFT_GRIP_R", r), - ("SWITCH_COLOR_LEFT_GRIP_G", g), - ("SWITCH_COLOR_LEFT_GRIP_B", b), - ("SWITCH_COLOR_RIGHT_GRIP_R", r), - ("SWITCH_COLOR_RIGHT_GRIP_G", g), - ("SWITCH_COLOR_RIGHT_GRIP_B", b), -]: - text = replace(macro, val, text) - -try: - with open(path, "w", encoding="utf-8") as f: - f.write(text) -except OSError as exc: - sys.stderr.write(f"Error writing {path}: {exc}\n") - sys.exit(1) -PY -} - -GRIP_MODE="none" -CUSTOM_COLOR="" - -while [[ $# -gt 0 ]]; do - case "$1" in - --random-grip-color) - ensure_single_color_mode "$GRIP_MODE" "random" - GRIP_MODE="random" - shift - ;; - --grip-color) - ensure_single_color_mode "$GRIP_MODE" "custom" - GRIP_MODE="custom" - CUSTOM_COLOR="${2-}" - if [[ -z "$CUSTOM_COLOR" ]]; then - echo "Error: --grip-color requires a hex value like FF8800." >&2 - exit 1 - fi - shift 2 - ;; - -h|--help) - usage - exit 0 - ;; - *) - echo "Unknown option: $1" >&2 - usage - exit 1 - ;; - esac -done - -if [[ "$GRIP_MODE" == "random" ]]; then - CUSTOM_COLOR="$(printf "%02X%02X%02X" $((RANDOM % 256)) $((RANDOM % 256)) $((RANDOM % 256)))" -elif [[ "$GRIP_MODE" == "custom" ]]; then - if ! [[ "$CUSTOM_COLOR" =~ ^[0-9A-Fa-f]{6}$ ]]; then - echo "Error: Color must be a 6-digit hex value like FF8800." >&2 - exit 1 - fi -fi - -if [[ "$GRIP_MODE" != "none" ]]; then - update_grip_colors "$CUSTOM_COLOR" - echo "Grip color set to #$CUSTOM_COLOR in controller_color_config.h" -fi - -cmake -S . -B build -DSWITCH_PICO_AUTOTEST=OFF -DSWITCH_PICO_LOG=OFF && cmake --build build -/Users/joey/.pico-sdk/picotool/2.2.0-a4/picotool/picotool load /Users/joey/Projects/switch-pico/build/switch-pico.elf -fx From 22cfe62c4167d777233b62a0b1e51e8d7519963e Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 14 Dec 2025 14:04:28 -0700 Subject: [PATCH 093/134] Add gyro branch ref --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d3bed2e..bb69056 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a 5. Connect the Pico to the Switch (dock USB-A or USB-C OTG); the Switch should see it as a wired Pro Controller. ## Planned features -- IMU support for motion controls (gyro/accelerometer passthrough for controllers that support it to the Switch). +- IMU support for motion controls (gyro/accelerometer passthrough for controllers that support it to the Switch) (WIP branch on `gyrov3`). ## Limitations - No motion controls/IMU passthrough yet (planned). From 6f8c698805cfd8634efc5eb3e355582a542c9f94 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 14 Dec 2025 14:04:28 -0700 Subject: [PATCH 094/134] Add gyro branch ref --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d3bed2e..bb69056 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a 5. Connect the Pico to the Switch (dock USB-A or USB-C OTG); the Switch should see it as a wired Pro Controller. ## Planned features -- IMU support for motion controls (gyro/accelerometer passthrough for controllers that support it to the Switch). +- IMU support for motion controls (gyro/accelerometer passthrough for controllers that support it to the Switch) (WIP branch on `gyrov3`). ## Limitations - No motion controls/IMU passthrough yet (planned). From e41a1746ba3f1744785bd56cf9848f81c51e0e73 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 14 Dec 2025 14:08:03 -0700 Subject: [PATCH 095/134] Add references --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index bb69056..e64a169 100644 --- a/README.md +++ b/README.md @@ -228,6 +228,12 @@ with SwitchUARTClient("/dev/cu.usbserial-0001") as client: ### Linux tips - You may need udev permissions for `/dev/ttyUSB*`/`/dev/ttyACM*` (add user to `dialout`/`uucp` or use `udev` rules). +## References +- GP2040-CE (controller firmware ecosystem): https://github.com/OpenStickCommunity/GP2040-CE +- nxbt (Switch controller research/tools): https://github.com/Brikwerk/nxbt +- Nintendo Switch Reverse Engineering notes: https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering +- `hid-nintendo` driver reference: https://github.com/DanielOgorchock/linux/blob/ogorchock/drivers/hid/hid-nintendo.c + ## Troubleshooting - **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, and `Pro Controller Wired Communication` is enabled on the Switch. - **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `switch_pico_bridge.controller_uart_bridge` if needed. From c00b096209a55c93bf780ca734f7fdd125525421 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 14 Dec 2025 14:08:03 -0700 Subject: [PATCH 096/134] Add references --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index bb69056..e64a169 100644 --- a/README.md +++ b/README.md @@ -228,6 +228,12 @@ with SwitchUARTClient("/dev/cu.usbserial-0001") as client: ### Linux tips - You may need udev permissions for `/dev/ttyUSB*`/`/dev/ttyACM*` (add user to `dialout`/`uucp` or use `udev` rules). +## References +- GP2040-CE (controller firmware ecosystem): https://github.com/OpenStickCommunity/GP2040-CE +- nxbt (Switch controller research/tools): https://github.com/Brikwerk/nxbt +- Nintendo Switch Reverse Engineering notes: https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering +- `hid-nintendo` driver reference: https://github.com/DanielOgorchock/linux/blob/ogorchock/drivers/hid/hid-nintendo.c + ## Troubleshooting - **No input on Switch**: verify UART wiring (Pico GPIO4/5), baud matches both sides, Pico flashed with current firmware, and `Pro Controller Wired Communication` is enabled on the Switch. - **Constant buzzing rumble**: the bridge filters small rumble payloads; ensure baud isn’t dropping bytes. Try lowering rumble scale in `switch_pico_bridge.controller_uart_bridge` if needed. From b04a6d29ca861dba1cd813d612259dc0e21aae97 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 14 Dec 2025 14:47:49 -0700 Subject: [PATCH 097/134] Add license --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..54aabb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Joey Yakimowich-Payne + +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. \ No newline at end of file From 62707e419baf3aedee685ca0ddecb8df884c04e5 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 14 Dec 2025 14:47:49 -0700 Subject: [PATCH 098/134] Add license --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..54aabb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Joey Yakimowich-Payne + +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. \ No newline at end of file From ae65251cb467f8a8daf46c20c7f51ed5a748c885 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 10 Jan 2026 23:19:13 -0700 Subject: [PATCH 099/134] Fix controllers --- .../controller_uart_bridge.py | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/switch_pico_bridge/controller_uart_bridge.py b/src/switch_pico_bridge/controller_uart_bridge.py index e223ed0..4f13857 100644 --- a/src/switch_pico_bridge/controller_uart_bridge.py +++ b/src/switch_pico_bridge/controller_uart_bridge.py @@ -214,6 +214,7 @@ class ControllerContext: last_rumble_energy: float = 0.0 rumble_active: bool = False axis_offsets: Dict[int, int] = field(default_factory=dict) + swap_abxy: bool = False def capture_stick_offsets(controller: sdl2.SDL_GameController) -> Dict[int, int]: @@ -395,12 +396,9 @@ def toggle_abxy_for_context(ctx: ControllerContext, config: BridgeConfig, consol if config.swap_abxy_global: console.print("[yellow]Global --swap-abxy is enabled; disable it to use per-controller toggles.[/yellow]") return - swapped = ctx.stable_id in config.swap_abxy_ids + swapped = ctx.swap_abxy action = "standard" if swapped else "swapped" - if swapped: - config.swap_abxy_ids.discard(ctx.stable_id) - else: - config.swap_abxy_ids.add(ctx.stable_id) + ctx.swap_abxy = not swapped console.print( f"[cyan]Controller {ctx.controller_index} ({controller_display_name(ctx)}, inst {ctx.instance_id}) now using {action} ABXY layout.[/cyan]" ) @@ -425,7 +423,7 @@ def prompt_swap_abxy_controller( table.add_column("GUID") table.add_column("Layout", justify="center") for idx, ctx in enumerate(controllers): - swapped = config.swap_abxy_global or (ctx.stable_id in config.swap_abxy_ids) + swapped = config.swap_abxy_global or ctx.swap_abxy state = "Swapped" if swapped else "Standard" if config.swap_abxy_global: state += " (global)" @@ -988,8 +986,7 @@ def open_initial_contexts( console.print(f"[red]Failed to open controller {index}: {exc}[/red]") continue stable_id = guid - if index in config.swap_abxy_indices: - config.swap_abxy_ids.add(stable_id) + should_swap = index in config.swap_abxy_indices or stable_id in config.swap_abxy_ids uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) @@ -1007,6 +1004,7 @@ def open_initial_contexts( stable_id=stable_id, port=port, uart=uart, + swap_abxy=should_swap, ) if config.zero_sticks: zero_context_sticks(ctx, console) @@ -1058,7 +1056,7 @@ def handle_button_event( return current_button_map = ( config.button_map_swapped - if (config.swap_abxy_global or ctx.stable_id in config.swap_abxy_ids) + if (config.swap_abxy_global or ctx.swap_abxy) else config.button_map_default ) button = event.cbutton.button @@ -1103,9 +1101,8 @@ def handle_device_added( console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") return stable_id = guid - # Promote any index-based swap flags to stable IDs on first sight. - if idx in config.swap_abxy_indices: - config.swap_abxy_ids.add(stable_id) + # Determine if this controller should have swapped ABXY based on index or GUID + should_swap = idx in config.swap_abxy_indices or stable_id in config.swap_abxy_ids uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) @@ -1123,6 +1120,7 @@ def handle_device_added( stable_id=stable_id, port=port, uart=uart, + swap_abxy=should_swap, ) if config.zero_sticks: zero_context_sticks(ctx, console) @@ -1163,7 +1161,7 @@ def service_contexts( for ctx in list(contexts.values()): current_button_map = ( config.button_map_swapped - if (config.swap_abxy_global or ctx.stable_id in config.swap_abxy_ids) + if (config.swap_abxy_global or ctx.swap_abxy) else config.button_map_default ) poll_controller_buttons(ctx, current_button_map) From 1a1d6db3df0bf18126e7b974d4403d1902847975 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 10 Jan 2026 23:19:13 -0700 Subject: [PATCH 100/134] Fix controllers --- .../controller_uart_bridge.py | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/switch_pico_bridge/controller_uart_bridge.py b/src/switch_pico_bridge/controller_uart_bridge.py index e223ed0..4f13857 100644 --- a/src/switch_pico_bridge/controller_uart_bridge.py +++ b/src/switch_pico_bridge/controller_uart_bridge.py @@ -214,6 +214,7 @@ class ControllerContext: last_rumble_energy: float = 0.0 rumble_active: bool = False axis_offsets: Dict[int, int] = field(default_factory=dict) + swap_abxy: bool = False def capture_stick_offsets(controller: sdl2.SDL_GameController) -> Dict[int, int]: @@ -395,12 +396,9 @@ def toggle_abxy_for_context(ctx: ControllerContext, config: BridgeConfig, consol if config.swap_abxy_global: console.print("[yellow]Global --swap-abxy is enabled; disable it to use per-controller toggles.[/yellow]") return - swapped = ctx.stable_id in config.swap_abxy_ids + swapped = ctx.swap_abxy action = "standard" if swapped else "swapped" - if swapped: - config.swap_abxy_ids.discard(ctx.stable_id) - else: - config.swap_abxy_ids.add(ctx.stable_id) + ctx.swap_abxy = not swapped console.print( f"[cyan]Controller {ctx.controller_index} ({controller_display_name(ctx)}, inst {ctx.instance_id}) now using {action} ABXY layout.[/cyan]" ) @@ -425,7 +423,7 @@ def prompt_swap_abxy_controller( table.add_column("GUID") table.add_column("Layout", justify="center") for idx, ctx in enumerate(controllers): - swapped = config.swap_abxy_global or (ctx.stable_id in config.swap_abxy_ids) + swapped = config.swap_abxy_global or ctx.swap_abxy state = "Swapped" if swapped else "Standard" if config.swap_abxy_global: state += " (global)" @@ -988,8 +986,7 @@ def open_initial_contexts( console.print(f"[red]Failed to open controller {index}: {exc}[/red]") continue stable_id = guid - if index in config.swap_abxy_indices: - config.swap_abxy_ids.add(stable_id) + should_swap = index in config.swap_abxy_indices or stable_id in config.swap_abxy_ids uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) @@ -1007,6 +1004,7 @@ def open_initial_contexts( stable_id=stable_id, port=port, uart=uart, + swap_abxy=should_swap, ) if config.zero_sticks: zero_context_sticks(ctx, console) @@ -1058,7 +1056,7 @@ def handle_button_event( return current_button_map = ( config.button_map_swapped - if (config.swap_abxy_global or ctx.stable_id in config.swap_abxy_ids) + if (config.swap_abxy_global or ctx.swap_abxy) else config.button_map_default ) button = event.cbutton.button @@ -1103,9 +1101,8 @@ def handle_device_added( console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") return stable_id = guid - # Promote any index-based swap flags to stable IDs on first sight. - if idx in config.swap_abxy_indices: - config.swap_abxy_ids.add(stable_id) + # Determine if this controller should have swapped ABXY based on index or GUID + should_swap = idx in config.swap_abxy_indices or stable_id in config.swap_abxy_ids uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) @@ -1123,6 +1120,7 @@ def handle_device_added( stable_id=stable_id, port=port, uart=uart, + swap_abxy=should_swap, ) if config.zero_sticks: zero_context_sticks(ctx, console) @@ -1163,7 +1161,7 @@ def service_contexts( for ctx in list(contexts.values()): current_button_map = ( config.button_map_swapped - if (config.swap_abxy_global or ctx.stable_id in config.swap_abxy_ids) + if (config.swap_abxy_global or ctx.swap_abxy) else config.button_map_default ) poll_controller_buttons(ctx, current_button_map) From 2604ff274bd452c55848c93f9bb117b4fc324407 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 11:50:25 -0600 Subject: [PATCH 101/134] feat(uart): add v2 protocol with IMU sample support - Add UART_PROTOCOL_VERSION=2, IMUSample dataclass, conversion constants - Rewrite SwitchReport.to_bytes() for versioned v2 framing with checksum - Add ACCEL_LSB_PER_G=4096, GYRO_LSB_PER_RAD_S=818.5, MS2_PER_G constants - Add SENSOR_ACCEL/SENSOR_GYRO SDL type constants with fallback - Add protocol round-trip tests (8 tests, all passing) --- src/switch_pico_bridge/switch_pico_uart.py | 97 ++++++++++++++--- tests/__init__.py | 0 tests/test_uart_protocol.py | 120 +++++++++++++++++++++ 3 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/test_uart_protocol.py diff --git a/src/switch_pico_bridge/switch_pico_uart.py b/src/switch_pico_bridge/switch_pico_uart.py index eb4fa25..2fdab53 100644 --- a/src/switch_pico_bridge/switch_pico_uart.py +++ b/src/switch_pico_bridge/switch_pico_uart.py @@ -12,6 +12,7 @@ depending on SDL. It mirrors the framing in ``switch-pico.cpp``: from __future__ import annotations +import math import struct import time import threading @@ -23,9 +24,28 @@ import serial from serial.tools import list_ports, list_ports_common UART_HEADER = 0xAA +UART_PROTOCOL_VERSION = 0x02 RUMBLE_HEADER = 0xBB RUMBLE_TYPE_RUMBLE = 0x01 UART_BAUD = 921600 +IMU_SAMPLES_PER_REPORT = 3 + +MS2_PER_G = 9.80665 +RAD_TO_DEG = 180.0 / math.pi +ACCEL_LSB_PER_G = 4096.0 +GYRO_LSB_PER_RAD_S = 818.5 + +try: + import sdl2 as _sdl2 # type: ignore[import-not-found] + + _sensor_accel = getattr(_sdl2, "SDL_SENSOR_ACCEL", 1) + _sensor_gyro = getattr(_sdl2, "SDL_SENSOR_GYRO", 2) +except ImportError: + _sensor_accel = 1 + _sensor_gyro = 2 + +SENSOR_ACCEL: int = _sensor_accel +SENSOR_GYRO: int = _sensor_gyro class SwitchButton(IntFlag): @@ -62,9 +82,9 @@ def _is_usb_serial_path(path: str) -> bool: """Heuristic for USB serial path prefixes.""" lower = path.lower() usb_prefixes = ( - "/dev/ttyusb", # Linux USB serial - "/dev/ttyacm", # Linux CDC ACM - "/dev/cu.usb", # macOS cu/tty USB adapters + "/dev/ttyusb", # Linux USB serial + "/dev/ttyacm", # Linux CDC ACM + "/dev/cu.usb", # macOS cu/tty USB adapters "/dev/tty.usb", ) if lower.startswith(usb_prefixes): @@ -144,6 +164,7 @@ def first_serial_port( return None return ports[0]["device"] + def clamp_byte(value: Union[int, float]) -> int: """Clamp a numeric value to the 0-255 byte range.""" return max(0, min(255, int(value))) @@ -202,6 +223,21 @@ def str_to_dpad(flags: Mapping[str, bool]) -> SwitchDpad: return SwitchDpad.CENTER +def compute_checksum(data: bytes) -> int: + """Compute UART checksum as sum of bytes modulo 256.""" + return sum(data) & 0xFF + + +@dataclass +class IMUSample: + accel_x: int = 0 + accel_y: int = 0 + accel_z: int = 0 + gyro_x: int = 0 + gyro_y: int = 0 + gyro_z: int = 0 + + @dataclass class SwitchReport: buttons: int = 0 @@ -210,13 +246,38 @@ class SwitchReport: ly: int = 128 rx: int = 128 ry: int = 128 + imu_samples: List[IMUSample] = field(default_factory=list) def to_bytes(self) -> bytes: - """Serialize the report into the UART packet format.""" - return struct.pack( - " None: @@ -267,15 +328,15 @@ class PicoUART: del self._buffer[:start] return None - frame = self._buffer[start:start + 11] - checksum = sum(frame[:10]) & 0xFF + frame = self._buffer[start : start + 11] + checksum = compute_checksum(bytes(frame[:10])) if frame[1] == RUMBLE_TYPE_RUMBLE and checksum == frame[10]: payload = bytes(frame[2:10]) - del self._buffer[:start + 11] + del self._buffer[: start + 11] return payload - del self._buffer[:start + 1] + del self._buffer[: start + 1] def close(self) -> None: """Close the UART connection.""" @@ -434,14 +495,20 @@ class SwitchUARTClient: self.state.move_right_stick(x, y) self.send() - def press_for(self, duration: float, *buttons: SwitchButton | SwitchDpad | int) -> None: + def press_for( + self, duration: float, *buttons: SwitchButton | SwitchDpad | int + ) -> None: """Press buttons/hat for a duration, then release.""" self.press(*buttons) time.sleep(max(0.0, duration)) self.release(*buttons) def move_left_stick_for( - self, x: Union[int, float], y: Union[int, float], duration: float, neutral_after: bool = True + self, + x: Union[int, float], + y: Union[int, float], + duration: float, + neutral_after: bool = True, ) -> None: """Move left stick for a duration, optionally returning it to neutral afterward.""" self.move_left_stick(x, y) @@ -451,7 +518,11 @@ class SwitchUARTClient: self.send() def move_right_stick_for( - self, x: Union[int, float], y: Union[int, float], duration: float, neutral_after: bool = True + self, + x: Union[int, float], + y: Union[int, float], + duration: float, + neutral_after: bool = True, ) -> None: """Move right stick for a duration, optionally returning it to neutral afterward.""" self.move_right_stick(x, y) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_uart_protocol.py b/tests/test_uart_protocol.py new file mode 100644 index 0000000..be3f8bd --- /dev/null +++ b/tests/test_uart_protocol.py @@ -0,0 +1,120 @@ +"""Tests for UART v2 protocol serialization in switch_pico_uart.""" + +import struct +import pytest +from switch_pico_bridge.switch_pico_uart import ( + SwitchReport, + IMUSample, + SwitchDpad, + UART_HEADER, + UART_PROTOCOL_VERSION, + ACCEL_LSB_PER_G, + GYRO_LSB_PER_RAD_S, + MS2_PER_G, + compute_checksum, +) + + +def test_v2_frame_with_imu_samples(): + """V2 frame with 3 IMU samples should be 48 bytes with correct layout.""" + r = SwitchReport( + buttons=0, + imu_samples=[ + IMUSample(100, -200, 4096, 50, -50, 0), + IMUSample(101, -201, 4097, 51, -51, 1), + IMUSample(102, -202, 4098, 52, -52, 2), + ], + ) + data = r.to_bytes() + assert len(data) == 48, f"Expected 48 bytes, got {len(data)}" + assert data[0] == UART_HEADER # 0xAA + assert data[1] == UART_PROTOCOL_VERSION # 0x02 + assert data[2] == 44 # payload_len + assert data[10] == 3 # imu_count + # Verify checksum + assert data[-1] == compute_checksum(data[:-1]) + # Verify first sample accel_x (int16 LE at byte 11) + ax0 = struct.unpack_from("3 IMU samples should cap at 3.""" + samples = [IMUSample(i, 0, 0, 0, 0, 0) for i in range(5)] + r = SwitchReport(imu_samples=samples) + data = r.to_bytes() + assert len(data) == 48 # 3 samples, not 5 + assert data[10] == 3 + assert data[2] == 44 # payload_len for 3 samples From fa0ddcd10f09b990cfed3c35b92a81e7e91c55e1 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 11:50:25 -0600 Subject: [PATCH 102/134] feat(uart): add v2 protocol with IMU sample support - Add UART_PROTOCOL_VERSION=2, IMUSample dataclass, conversion constants - Rewrite SwitchReport.to_bytes() for versioned v2 framing with checksum - Add ACCEL_LSB_PER_G=4096, GYRO_LSB_PER_RAD_S=818.5, MS2_PER_G constants - Add SENSOR_ACCEL/SENSOR_GYRO SDL type constants with fallback - Add protocol round-trip tests (8 tests, all passing) --- src/switch_pico_bridge/switch_pico_uart.py | 97 ++++++++++++++--- tests/__init__.py | 0 tests/test_uart_protocol.py | 120 +++++++++++++++++++++ 3 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/test_uart_protocol.py diff --git a/src/switch_pico_bridge/switch_pico_uart.py b/src/switch_pico_bridge/switch_pico_uart.py index eb4fa25..2fdab53 100644 --- a/src/switch_pico_bridge/switch_pico_uart.py +++ b/src/switch_pico_bridge/switch_pico_uart.py @@ -12,6 +12,7 @@ depending on SDL. It mirrors the framing in ``switch-pico.cpp``: from __future__ import annotations +import math import struct import time import threading @@ -23,9 +24,28 @@ import serial from serial.tools import list_ports, list_ports_common UART_HEADER = 0xAA +UART_PROTOCOL_VERSION = 0x02 RUMBLE_HEADER = 0xBB RUMBLE_TYPE_RUMBLE = 0x01 UART_BAUD = 921600 +IMU_SAMPLES_PER_REPORT = 3 + +MS2_PER_G = 9.80665 +RAD_TO_DEG = 180.0 / math.pi +ACCEL_LSB_PER_G = 4096.0 +GYRO_LSB_PER_RAD_S = 818.5 + +try: + import sdl2 as _sdl2 # type: ignore[import-not-found] + + _sensor_accel = getattr(_sdl2, "SDL_SENSOR_ACCEL", 1) + _sensor_gyro = getattr(_sdl2, "SDL_SENSOR_GYRO", 2) +except ImportError: + _sensor_accel = 1 + _sensor_gyro = 2 + +SENSOR_ACCEL: int = _sensor_accel +SENSOR_GYRO: int = _sensor_gyro class SwitchButton(IntFlag): @@ -62,9 +82,9 @@ def _is_usb_serial_path(path: str) -> bool: """Heuristic for USB serial path prefixes.""" lower = path.lower() usb_prefixes = ( - "/dev/ttyusb", # Linux USB serial - "/dev/ttyacm", # Linux CDC ACM - "/dev/cu.usb", # macOS cu/tty USB adapters + "/dev/ttyusb", # Linux USB serial + "/dev/ttyacm", # Linux CDC ACM + "/dev/cu.usb", # macOS cu/tty USB adapters "/dev/tty.usb", ) if lower.startswith(usb_prefixes): @@ -144,6 +164,7 @@ def first_serial_port( return None return ports[0]["device"] + def clamp_byte(value: Union[int, float]) -> int: """Clamp a numeric value to the 0-255 byte range.""" return max(0, min(255, int(value))) @@ -202,6 +223,21 @@ def str_to_dpad(flags: Mapping[str, bool]) -> SwitchDpad: return SwitchDpad.CENTER +def compute_checksum(data: bytes) -> int: + """Compute UART checksum as sum of bytes modulo 256.""" + return sum(data) & 0xFF + + +@dataclass +class IMUSample: + accel_x: int = 0 + accel_y: int = 0 + accel_z: int = 0 + gyro_x: int = 0 + gyro_y: int = 0 + gyro_z: int = 0 + + @dataclass class SwitchReport: buttons: int = 0 @@ -210,13 +246,38 @@ class SwitchReport: ly: int = 128 rx: int = 128 ry: int = 128 + imu_samples: List[IMUSample] = field(default_factory=list) def to_bytes(self) -> bytes: - """Serialize the report into the UART packet format.""" - return struct.pack( - " None: @@ -267,15 +328,15 @@ class PicoUART: del self._buffer[:start] return None - frame = self._buffer[start:start + 11] - checksum = sum(frame[:10]) & 0xFF + frame = self._buffer[start : start + 11] + checksum = compute_checksum(bytes(frame[:10])) if frame[1] == RUMBLE_TYPE_RUMBLE and checksum == frame[10]: payload = bytes(frame[2:10]) - del self._buffer[:start + 11] + del self._buffer[: start + 11] return payload - del self._buffer[:start + 1] + del self._buffer[: start + 1] def close(self) -> None: """Close the UART connection.""" @@ -434,14 +495,20 @@ class SwitchUARTClient: self.state.move_right_stick(x, y) self.send() - def press_for(self, duration: float, *buttons: SwitchButton | SwitchDpad | int) -> None: + def press_for( + self, duration: float, *buttons: SwitchButton | SwitchDpad | int + ) -> None: """Press buttons/hat for a duration, then release.""" self.press(*buttons) time.sleep(max(0.0, duration)) self.release(*buttons) def move_left_stick_for( - self, x: Union[int, float], y: Union[int, float], duration: float, neutral_after: bool = True + self, + x: Union[int, float], + y: Union[int, float], + duration: float, + neutral_after: bool = True, ) -> None: """Move left stick for a duration, optionally returning it to neutral afterward.""" self.move_left_stick(x, y) @@ -451,7 +518,11 @@ class SwitchUARTClient: self.send() def move_right_stick_for( - self, x: Union[int, float], y: Union[int, float], duration: float, neutral_after: bool = True + self, + x: Union[int, float], + y: Union[int, float], + duration: float, + neutral_after: bool = True, ) -> None: """Move right stick for a duration, optionally returning it to neutral afterward.""" self.move_right_stick(x, y) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_uart_protocol.py b/tests/test_uart_protocol.py new file mode 100644 index 0000000..be3f8bd --- /dev/null +++ b/tests/test_uart_protocol.py @@ -0,0 +1,120 @@ +"""Tests for UART v2 protocol serialization in switch_pico_uart.""" + +import struct +import pytest +from switch_pico_bridge.switch_pico_uart import ( + SwitchReport, + IMUSample, + SwitchDpad, + UART_HEADER, + UART_PROTOCOL_VERSION, + ACCEL_LSB_PER_G, + GYRO_LSB_PER_RAD_S, + MS2_PER_G, + compute_checksum, +) + + +def test_v2_frame_with_imu_samples(): + """V2 frame with 3 IMU samples should be 48 bytes with correct layout.""" + r = SwitchReport( + buttons=0, + imu_samples=[ + IMUSample(100, -200, 4096, 50, -50, 0), + IMUSample(101, -201, 4097, 51, -51, 1), + IMUSample(102, -202, 4098, 52, -52, 2), + ], + ) + data = r.to_bytes() + assert len(data) == 48, f"Expected 48 bytes, got {len(data)}" + assert data[0] == UART_HEADER # 0xAA + assert data[1] == UART_PROTOCOL_VERSION # 0x02 + assert data[2] == 44 # payload_len + assert data[10] == 3 # imu_count + # Verify checksum + assert data[-1] == compute_checksum(data[:-1]) + # Verify first sample accel_x (int16 LE at byte 11) + ax0 = struct.unpack_from("3 IMU samples should cap at 3.""" + samples = [IMUSample(i, 0, 0, 0, 0, 0) for i in range(5)] + r = SwitchReport(imu_samples=samples) + data = r.to_bytes() + assert len(data) == 48 # 3 samples, not 5 + assert data[10] == 3 + assert data[2] == 44 # payload_len for 3 samples From 0db04be85883ec0fb50e47985c9ea61f94503408 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 11:50:32 -0600 Subject: [PATCH 103/134] feat(firmware): add IMU data structures and UART v2 parser - Add SwitchImuSample struct and IMU fields to SwitchInputState - Add fill_imu_report_data() to pack samples into imuData[36] - Rewrite switch_pro_apply_uart_packet() for v2 with checksum validation - Enlarge poll_uart_frames() buffer to 64 bytes for variable-length frames - Clear imu_sample_count after each USB report send (prevent stale IMU) --- switch-pico.cpp | 40 +++++++++++---- switch_pro_driver.cpp | 111 ++++++++++++++++++++++++++++++++++-------- switch_pro_driver.h | 12 +++++ 3 files changed, 135 insertions(+), 28 deletions(-) diff --git a/switch-pico.cpp b/switch-pico.cpp index 5efd6e9..22beaef 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -61,11 +61,13 @@ static void on_rumble_from_switch(const uint8_t rumble[8]) { } // Consume UART bytes and forward complete frames to the Switch Pro driver. -static void poll_uart_frames() { - static uint8_t buffer[8]; +static bool poll_uart_frames() { + static uint8_t buffer[64]; static uint8_t index = 0; + static uint8_t expected_len = 0; static absolute_time_t last_byte_time = {0}; static bool has_last_byte = false; + bool new_data = false; while (uart_is_readable(UART_ID)) { uint8_t byte = uart_getc(UART_ID); @@ -73,6 +75,7 @@ static void poll_uart_frames() { uint64_t now = to_ms_since_boot(get_absolute_time()); if (has_last_byte && (now - to_ms_since_boot(last_byte_time)) > 20) { index = 0; // stale data, restart frame + expected_len = 0; } last_byte_time = get_absolute_time(); has_last_byte = true; @@ -83,11 +86,26 @@ static void poll_uart_frames() { } } - buffer[index++] = byte; if (index >= sizeof(buffer)) { + index = 0; + expected_len = 0; + } + + buffer[index++] = byte; + if (index == 3) { + expected_len = static_cast(buffer[2] + 4u); + if (expected_len < 12 || expected_len > sizeof(buffer)) { + index = 0; + expected_len = 0; + continue; + } + } + + if (expected_len > 0 && index >= expected_len) { SwitchInputState parsed{}; - if (switch_pro_apply_uart_packet(buffer, sizeof(buffer), &parsed)) { + if (switch_pro_apply_uart_packet(buffer, expected_len, &parsed)) { g_user_state = parsed; + new_data = true; LOG_PRINTF("[UART] packet buttons=0x%04x hat=%u lx=%u ly=%u rx=%u ry=%u\n", (parsed.button_a ? SWITCH_PRO_MASK_A : 0) | (parsed.button_b ? SWITCH_PRO_MASK_B : 0) | @@ -104,14 +122,17 @@ static void poll_uart_frames() { (parsed.button_l3 ? SWITCH_PRO_MASK_L3 : 0) | (parsed.button_r3 ? SWITCH_PRO_MASK_R3 : 0), parsed.dpad_up ? SWITCH_PRO_HAT_UP : - parsed.dpad_down ? SWITCH_PRO_HAT_DOWN : - parsed.dpad_left ? SWITCH_PRO_HAT_LEFT : - parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING, - parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8); + parsed.dpad_down ? SWITCH_PRO_HAT_DOWN : + parsed.dpad_left ? SWITCH_PRO_HAT_LEFT : + parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING, + parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8); } index = 0; + expected_len = 0; } } + + return new_data; } static void log_usb_state() { @@ -146,7 +167,8 @@ int main() { while (true) { tud_task(); // USB device tasks - poll_uart_frames(); // Pull controller state from UART1 + bool new_data = poll_uart_frames(); // Pull controller state from UART1 + (void)new_data; SwitchInputState state = g_user_state; switch_pro_set_input(state); switch_pro_task(); // Push state to the Switch host diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index 138ba8f..ee68f68 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -184,12 +184,39 @@ static std::map spi_flash_data = { static inline uint16_t scale16To12(uint16_t pos) { return pos >> 4; } +static void fill_imu_report_data(const SwitchInputState& state) { + if (state.imu_sample_count == 0) { + memset(switch_report.imuData, 0x00, sizeof(switch_report.imuData)); + return; + } + uint8_t sample_count = state.imu_sample_count > 3 ? 3 : state.imu_sample_count; + // If fewer than 3 samples, duplicate the last one to fill all 3 slots + uint8_t* dst = switch_report.imuData; + for (uint8_t i = 0; i < 3; ++i) { + const SwitchImuSample& s = (i < sample_count) ? state.imu_samples[i] : state.imu_samples[sample_count - 1]; + dst[0] = static_cast(s.accel_x & 0xFF); + dst[1] = static_cast((s.accel_x >> 8) & 0xFF); + dst[2] = static_cast(s.accel_y & 0xFF); + dst[3] = static_cast((s.accel_y >> 8) & 0xFF); + dst[4] = static_cast(s.accel_z & 0xFF); + dst[5] = static_cast((s.accel_z >> 8) & 0xFF); + dst[6] = static_cast(s.gyro_x & 0xFF); + dst[7] = static_cast((s.gyro_x >> 8) & 0xFF); + dst[8] = static_cast(s.gyro_y & 0xFF); + dst[9] = static_cast((s.gyro_y >> 8) & 0xFF); + dst[10] = static_cast(s.gyro_z & 0xFF); + dst[11] = static_cast((s.gyro_z >> 8) & 0xFF); + dst += 12; + } +} + static SwitchInputState make_neutral_state() { SwitchInputState s{}; s.lx = SWITCH_PRO_JOYSTICK_MID; s.ly = SWITCH_PRO_JOYSTICK_MID; s.rx = SWITCH_PRO_JOYSTICK_MID; s.ry = SWITCH_PRO_JOYSTICK_MID; + s.imu_sample_count = 0; return s; } @@ -485,6 +512,7 @@ static void update_switch_report_from_state() { switch_report.inputs.rightStick.setX(std::min(std::max(scaleRightStickX,rightMinX), rightMaxX)); switch_report.inputs.rightStick.setY(-std::min(std::max(scaleRightStickY,rightMinY), rightMaxY)); + fill_imu_report_data(g_input_state); switch_report.rumbleReport = 0x09; } @@ -594,14 +622,13 @@ void switch_pro_task() { switch_report.timestamp = last_report_counter; void * inputReport = &switch_report; uint16_t report_size = sizeof(switch_report); - if (memcmp(last_report, inputReport, report_size) != 0) { - if (tud_hid_ready() && send_report(0, inputReport, report_size) == true ) { - memcpy(last_report, inputReport, report_size); - report_sent = true; - } - - last_report_timer = now; + if (tud_hid_ready() && send_report(0, inputReport, report_size) == true ) { + memcpy(last_report, inputReport, report_size); + g_input_state.imu_sample_count = 0; + report_sent = true; } + + last_report_timer = now; } } else { if (!is_initialized) { @@ -617,24 +644,71 @@ void switch_pro_task() { } bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchInputState* out_state) { - // Packet format: 0xAA, buttons(2 LE), hat, lx, ly, rx, ry - if (length < 8 || packet[0] != 0xAA) { + // v2 format: 0xAA + 0x02 + payload_len + payload... + checksum + if (length < 12) { + return false; + } + if (packet[0] != 0xAA) { + return false; + } + if (packet[1] != 0x02) { + return false; + } + + uint8_t payload_len = packet[2]; + if ((uint16_t)payload_len + 4u != length) { + return false; + } + + uint16_t sum = 0; + for (uint16_t i = 0; i < (uint16_t)(3u + payload_len); ++i) { + sum += packet[i]; + } + if ((sum & 0xFF) != packet[length - 1]) { + return false; + } + + // payload: buttons(2 LE), hat, lx, ly, rx, ry, imu_count, [imu_samples...] + if (payload_len < 8) { return false; } SwitchProOutReport out{}; - out.buttons = static_cast(packet[1]) | (static_cast(packet[2]) << 8); - out.hat = packet[3]; - out.lx = packet[4]; - out.ly = packet[5]; - out.rx = packet[6]; - out.ry = packet[7]; + out.buttons = static_cast(packet[3]) | (static_cast(packet[4]) << 8); + out.hat = packet[5]; + out.lx = packet[6]; + out.ly = packet[7]; + out.rx = packet[8]; + out.ry = packet[9]; + uint8_t imu_count = packet[10]; + if (imu_count > 3) { + imu_count = 3; + } + + uint16_t required_payload_len = static_cast(8u + static_cast(imu_count) * 12u); + if (payload_len < required_payload_len) { + return false; + } auto expand_axis = [](uint8_t v) -> uint16_t { return static_cast(v) << 8 | v; }; SwitchInputState state = make_neutral_state(); + state.imu_sample_count = imu_count; + + auto read_int16 = [](const uint8_t* src) -> int16_t { + return static_cast(static_cast(src[0]) | (static_cast(src[1]) << 8)); + }; + for (uint8_t i = 0; i < imu_count; ++i) { + const uint8_t* base = &packet[11 + i * 12]; + state.imu_samples[i].accel_x = read_int16(base + 0); + state.imu_samples[i].accel_y = read_int16(base + 2); + state.imu_samples[i].accel_z = read_int16(base + 4); + state.imu_samples[i].gyro_x = read_int16(base + 6); + state.imu_samples[i].gyro_y = read_int16(base + 8); + state.imu_samples[i].gyro_z = read_int16(base + 10); + } switch (out.hat) { case SWITCH_PRO_HAT_UP: state.dpad_up = true; break; @@ -668,11 +742,10 @@ bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchI state.rx = expand_axis(out.rx); state.ry = expand_axis(out.ry); - if (out_state) { - *out_state = state; - } else { - switch_pro_set_input(state); + if (!out_state) { + return false; } + *out_state = state; return true; } diff --git a/switch_pro_driver.h b/switch_pro_driver.h index 73d8582..951d68b 100644 --- a/switch_pro_driver.h +++ b/switch_pro_driver.h @@ -10,6 +10,15 @@ #include #include "switch_pro_descriptors.h" +typedef struct { + int16_t accel_x; + int16_t accel_y; + int16_t accel_z; + int16_t gyro_x; + int16_t gyro_y; + int16_t gyro_z; +} SwitchImuSample; + typedef struct { bool dpad_up; bool dpad_down; @@ -35,6 +44,9 @@ typedef struct { uint16_t ly; uint16_t rx; uint16_t ry; + + uint8_t imu_sample_count; // 0-3 + SwitchImuSample imu_samples[3]; } SwitchInputState; // Initialize USB state and calibration before entering the main loop. From 4501528371ea16f7f9e14b374ff026af66240682 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 11:50:32 -0600 Subject: [PATCH 104/134] feat(firmware): add IMU data structures and UART v2 parser - Add SwitchImuSample struct and IMU fields to SwitchInputState - Add fill_imu_report_data() to pack samples into imuData[36] - Rewrite switch_pro_apply_uart_packet() for v2 with checksum validation - Enlarge poll_uart_frames() buffer to 64 bytes for variable-length frames - Clear imu_sample_count after each USB report send (prevent stale IMU) --- switch-pico.cpp | 40 +++++++++++---- switch_pro_driver.cpp | 111 ++++++++++++++++++++++++++++++++++-------- switch_pro_driver.h | 12 +++++ 3 files changed, 135 insertions(+), 28 deletions(-) diff --git a/switch-pico.cpp b/switch-pico.cpp index 5efd6e9..22beaef 100644 --- a/switch-pico.cpp +++ b/switch-pico.cpp @@ -61,11 +61,13 @@ static void on_rumble_from_switch(const uint8_t rumble[8]) { } // Consume UART bytes and forward complete frames to the Switch Pro driver. -static void poll_uart_frames() { - static uint8_t buffer[8]; +static bool poll_uart_frames() { + static uint8_t buffer[64]; static uint8_t index = 0; + static uint8_t expected_len = 0; static absolute_time_t last_byte_time = {0}; static bool has_last_byte = false; + bool new_data = false; while (uart_is_readable(UART_ID)) { uint8_t byte = uart_getc(UART_ID); @@ -73,6 +75,7 @@ static void poll_uart_frames() { uint64_t now = to_ms_since_boot(get_absolute_time()); if (has_last_byte && (now - to_ms_since_boot(last_byte_time)) > 20) { index = 0; // stale data, restart frame + expected_len = 0; } last_byte_time = get_absolute_time(); has_last_byte = true; @@ -83,11 +86,26 @@ static void poll_uart_frames() { } } - buffer[index++] = byte; if (index >= sizeof(buffer)) { + index = 0; + expected_len = 0; + } + + buffer[index++] = byte; + if (index == 3) { + expected_len = static_cast(buffer[2] + 4u); + if (expected_len < 12 || expected_len > sizeof(buffer)) { + index = 0; + expected_len = 0; + continue; + } + } + + if (expected_len > 0 && index >= expected_len) { SwitchInputState parsed{}; - if (switch_pro_apply_uart_packet(buffer, sizeof(buffer), &parsed)) { + if (switch_pro_apply_uart_packet(buffer, expected_len, &parsed)) { g_user_state = parsed; + new_data = true; LOG_PRINTF("[UART] packet buttons=0x%04x hat=%u lx=%u ly=%u rx=%u ry=%u\n", (parsed.button_a ? SWITCH_PRO_MASK_A : 0) | (parsed.button_b ? SWITCH_PRO_MASK_B : 0) | @@ -104,14 +122,17 @@ static void poll_uart_frames() { (parsed.button_l3 ? SWITCH_PRO_MASK_L3 : 0) | (parsed.button_r3 ? SWITCH_PRO_MASK_R3 : 0), parsed.dpad_up ? SWITCH_PRO_HAT_UP : - parsed.dpad_down ? SWITCH_PRO_HAT_DOWN : - parsed.dpad_left ? SWITCH_PRO_HAT_LEFT : - parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING, - parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8); + parsed.dpad_down ? SWITCH_PRO_HAT_DOWN : + parsed.dpad_left ? SWITCH_PRO_HAT_LEFT : + parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING, + parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8); } index = 0; + expected_len = 0; } } + + return new_data; } static void log_usb_state() { @@ -146,7 +167,8 @@ int main() { while (true) { tud_task(); // USB device tasks - poll_uart_frames(); // Pull controller state from UART1 + bool new_data = poll_uart_frames(); // Pull controller state from UART1 + (void)new_data; SwitchInputState state = g_user_state; switch_pro_set_input(state); switch_pro_task(); // Push state to the Switch host diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index 138ba8f..ee68f68 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -184,12 +184,39 @@ static std::map spi_flash_data = { static inline uint16_t scale16To12(uint16_t pos) { return pos >> 4; } +static void fill_imu_report_data(const SwitchInputState& state) { + if (state.imu_sample_count == 0) { + memset(switch_report.imuData, 0x00, sizeof(switch_report.imuData)); + return; + } + uint8_t sample_count = state.imu_sample_count > 3 ? 3 : state.imu_sample_count; + // If fewer than 3 samples, duplicate the last one to fill all 3 slots + uint8_t* dst = switch_report.imuData; + for (uint8_t i = 0; i < 3; ++i) { + const SwitchImuSample& s = (i < sample_count) ? state.imu_samples[i] : state.imu_samples[sample_count - 1]; + dst[0] = static_cast(s.accel_x & 0xFF); + dst[1] = static_cast((s.accel_x >> 8) & 0xFF); + dst[2] = static_cast(s.accel_y & 0xFF); + dst[3] = static_cast((s.accel_y >> 8) & 0xFF); + dst[4] = static_cast(s.accel_z & 0xFF); + dst[5] = static_cast((s.accel_z >> 8) & 0xFF); + dst[6] = static_cast(s.gyro_x & 0xFF); + dst[7] = static_cast((s.gyro_x >> 8) & 0xFF); + dst[8] = static_cast(s.gyro_y & 0xFF); + dst[9] = static_cast((s.gyro_y >> 8) & 0xFF); + dst[10] = static_cast(s.gyro_z & 0xFF); + dst[11] = static_cast((s.gyro_z >> 8) & 0xFF); + dst += 12; + } +} + static SwitchInputState make_neutral_state() { SwitchInputState s{}; s.lx = SWITCH_PRO_JOYSTICK_MID; s.ly = SWITCH_PRO_JOYSTICK_MID; s.rx = SWITCH_PRO_JOYSTICK_MID; s.ry = SWITCH_PRO_JOYSTICK_MID; + s.imu_sample_count = 0; return s; } @@ -485,6 +512,7 @@ static void update_switch_report_from_state() { switch_report.inputs.rightStick.setX(std::min(std::max(scaleRightStickX,rightMinX), rightMaxX)); switch_report.inputs.rightStick.setY(-std::min(std::max(scaleRightStickY,rightMinY), rightMaxY)); + fill_imu_report_data(g_input_state); switch_report.rumbleReport = 0x09; } @@ -594,14 +622,13 @@ void switch_pro_task() { switch_report.timestamp = last_report_counter; void * inputReport = &switch_report; uint16_t report_size = sizeof(switch_report); - if (memcmp(last_report, inputReport, report_size) != 0) { - if (tud_hid_ready() && send_report(0, inputReport, report_size) == true ) { - memcpy(last_report, inputReport, report_size); - report_sent = true; - } - - last_report_timer = now; + if (tud_hid_ready() && send_report(0, inputReport, report_size) == true ) { + memcpy(last_report, inputReport, report_size); + g_input_state.imu_sample_count = 0; + report_sent = true; } + + last_report_timer = now; } } else { if (!is_initialized) { @@ -617,24 +644,71 @@ void switch_pro_task() { } bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchInputState* out_state) { - // Packet format: 0xAA, buttons(2 LE), hat, lx, ly, rx, ry - if (length < 8 || packet[0] != 0xAA) { + // v2 format: 0xAA + 0x02 + payload_len + payload... + checksum + if (length < 12) { + return false; + } + if (packet[0] != 0xAA) { + return false; + } + if (packet[1] != 0x02) { + return false; + } + + uint8_t payload_len = packet[2]; + if ((uint16_t)payload_len + 4u != length) { + return false; + } + + uint16_t sum = 0; + for (uint16_t i = 0; i < (uint16_t)(3u + payload_len); ++i) { + sum += packet[i]; + } + if ((sum & 0xFF) != packet[length - 1]) { + return false; + } + + // payload: buttons(2 LE), hat, lx, ly, rx, ry, imu_count, [imu_samples...] + if (payload_len < 8) { return false; } SwitchProOutReport out{}; - out.buttons = static_cast(packet[1]) | (static_cast(packet[2]) << 8); - out.hat = packet[3]; - out.lx = packet[4]; - out.ly = packet[5]; - out.rx = packet[6]; - out.ry = packet[7]; + out.buttons = static_cast(packet[3]) | (static_cast(packet[4]) << 8); + out.hat = packet[5]; + out.lx = packet[6]; + out.ly = packet[7]; + out.rx = packet[8]; + out.ry = packet[9]; + uint8_t imu_count = packet[10]; + if (imu_count > 3) { + imu_count = 3; + } + + uint16_t required_payload_len = static_cast(8u + static_cast(imu_count) * 12u); + if (payload_len < required_payload_len) { + return false; + } auto expand_axis = [](uint8_t v) -> uint16_t { return static_cast(v) << 8 | v; }; SwitchInputState state = make_neutral_state(); + state.imu_sample_count = imu_count; + + auto read_int16 = [](const uint8_t* src) -> int16_t { + return static_cast(static_cast(src[0]) | (static_cast(src[1]) << 8)); + }; + for (uint8_t i = 0; i < imu_count; ++i) { + const uint8_t* base = &packet[11 + i * 12]; + state.imu_samples[i].accel_x = read_int16(base + 0); + state.imu_samples[i].accel_y = read_int16(base + 2); + state.imu_samples[i].accel_z = read_int16(base + 4); + state.imu_samples[i].gyro_x = read_int16(base + 6); + state.imu_samples[i].gyro_y = read_int16(base + 8); + state.imu_samples[i].gyro_z = read_int16(base + 10); + } switch (out.hat) { case SWITCH_PRO_HAT_UP: state.dpad_up = true; break; @@ -668,11 +742,10 @@ bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchI state.rx = expand_axis(out.rx); state.ry = expand_axis(out.ry); - if (out_state) { - *out_state = state; - } else { - switch_pro_set_input(state); + if (!out_state) { + return false; } + *out_state = state; return true; } diff --git a/switch_pro_driver.h b/switch_pro_driver.h index 73d8582..951d68b 100644 --- a/switch_pro_driver.h +++ b/switch_pro_driver.h @@ -10,6 +10,15 @@ #include #include "switch_pro_descriptors.h" +typedef struct { + int16_t accel_x; + int16_t accel_y; + int16_t accel_z; + int16_t gyro_x; + int16_t gyro_y; + int16_t gyro_z; +} SwitchImuSample; + typedef struct { bool dpad_up; bool dpad_down; @@ -35,6 +44,9 @@ typedef struct { uint16_t ly; uint16_t rx; uint16_t ry; + + uint8_t imu_sample_count; // 0-3 + SwitchImuSample imu_samples[3]; } SwitchInputState; // Initialize USB state and calibration before entering the main loop. From d81e8f90c03a9aa8d142e0afcc9474c95b6e0c49 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 11:50:40 -0600 Subject: [PATCH 105/134] feat(bridge): add SDL sensor reading and gyro bias calibration - Add initialize_controller_sensors() for SDL accel/gyro enable - Add handle_sensor_update() with direct axis passthrough (no remapping) - Add gyro bias calibration (200 samples, zero gyro until locked) - Add IMU sample FIFO with pop-to-report before UART send - Add --debug-imu, --no-imu, --gyro-scale CLI flags --- .../controller_uart_bridge.py | 491 +++++++++++++++--- 1 file changed, 417 insertions(+), 74 deletions(-) diff --git a/src/switch_pico_bridge/controller_uart_bridge.py b/src/switch_pico_bridge/controller_uart_bridge.py index 4f13857..71550f6 100644 --- a/src/switch_pico_bridge/controller_uart_bridge.py +++ b/src/switch_pico_bridge/controller_uart_bridge.py @@ -34,6 +34,14 @@ from rich.table import Table from .switch_pico_uart import ( UART_BAUD, + MS2_PER_G, + RAD_TO_DEG, + ACCEL_LSB_PER_G, + GYRO_LSB_PER_RAD_S, + SENSOR_ACCEL, + SENSOR_GYRO, + IMU_SAMPLES_PER_REPORT, + IMUSample, PicoUART, SwitchButton, SwitchDpad, @@ -49,9 +57,11 @@ RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this RUMBLE_MIN_ACTIVE = 0.40 # below this, rumble is treated as off/noise RUMBLE_SCALE = 1.0 -CONTROLLER_DB_URL_DEFAULT = ( - "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt" -) +CONTROLLER_DB_URL_DEFAULT = "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt" +SDL_TRUE = getattr(sdl2, "SDL_TRUE", 1) +SDL_CONTROLLERSENSORUPDATE = getattr(sdl2, "SDL_CONTROLLERSENSORUPDATE", 0x658) +GYRO_BIAS_SAMPLES = 200 +IMU_BUFFER_SIZE = 32 def parse_mapping(value: str) -> Tuple[int, str]: @@ -62,7 +72,9 @@ def parse_mapping(value: str) -> Tuple[int, str]: try: idx = int(idx_str, 10) except ValueError as exc: - raise argparse.ArgumentTypeError(f"Invalid controller index '{idx_str}'") from exc + raise argparse.ArgumentTypeError( + f"Invalid controller index '{idx_str}'" + ) from exc if not port: raise argparse.ArgumentTypeError("Serial port cannot be empty") return idx, port.strip() @@ -84,9 +96,13 @@ def download_controller_db(console: Console, destination: Path, url: str) -> boo try: destination.write_bytes(data) except Exception as exc: - console.print(f"[red]Failed to write controller database to {destination}: {exc}[/red]") + console.print( + f"[red]Failed to write controller database to {destination}: {exc}[/red]" + ) return False - console.print(f"[green]Updated controller database ({len(data)} bytes) at {destination}[/green]") + console.print( + f"[green]Updated controller database ({len(data)} bytes) at {destination}[/green]" + ) return True @@ -98,7 +114,9 @@ def parse_hotkey(value: str) -> str: if not value: return "" if len(value) != 1: - raise argparse.ArgumentTypeError("Hotkeys must be a single character (or empty to disable).") + raise argparse.ArgumentTypeError( + "Hotkeys must be a single character (or empty to disable)." + ) return value @@ -151,7 +169,9 @@ def interactive_pairing( mappings: List[Tuple[int, str]] = [] for controller_idx in controller_info: if not available: - console.print("[bold red]No more UART devices available for pairing.[/bold red]") + console.print( + "[bold red]No more UART devices available for pairing.[/bold red]" + ) break table = Table( @@ -174,7 +194,9 @@ def interactive_pairing( idx = int(selection) port = available.pop(idx) mappings.append((controller_idx, port["device"])) - console.print(f"[bold green]Paired controller {controller_idx} with {port['device']}[/bold green]") + console.print( + f"[bold green]Paired controller {controller_idx} with {port['device']}[/bold green]" + ) return mappings @@ -188,7 +210,7 @@ def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> float: return 0.0 # Attenuate to feel closer to a real controller; cap at ~25% strength. scale = RUMBLE_SCALE - low = int(min(1.0, left_norm * scale) * 0xFFFF) # SDL: low_frequency_rumble + low = int(min(1.0, left_norm * scale) * 0xFFFF) # SDL: low_frequency_rumble high = int(min(1.0, right_norm * scale) * 0xFFFF) # SDL: high_frequency_rumble duration = 10 sdl2.SDL_GameControllerRumble(controller, low, high, duration) @@ -204,9 +226,18 @@ class ControllerContext: port: Optional[str] uart: Optional[PicoUART] report: SwitchReport = field(default_factory=SwitchReport) - dpad: Dict[str, bool] = field(default_factory=lambda: {"up": False, "down": False, "left": False, "right": False}) + dpad: Dict[str, bool] = field( + default_factory=lambda: { + "up": False, + "down": False, + "left": False, + "right": False, + } + ) button_state: Dict[int, bool] = field(default_factory=dict) - last_trigger_state: Dict[str, bool] = field(default_factory=lambda: {"left": False, "right": False}) + last_trigger_state: Dict[str, bool] = field( + default_factory=lambda: {"left": False, "right": False} + ) last_send: float = 0.0 last_reopen_attempt: float = 0.0 last_rumble: float = 0.0 @@ -215,6 +246,16 @@ class ControllerContext: rumble_active: bool = False axis_offsets: Dict[int, int] = field(default_factory=dict) swap_abxy: bool = False + sensors_supported: bool = False + sensors_enabled: bool = False + imu_samples: List[IMUSample] = field(default_factory=list) + last_accel: Tuple[float, float, float] = (0.0, 0.0, 0.0) + gyro_bias_x: float = 0.0 + gyro_bias_y: float = 0.0 + gyro_bias_z: float = 0.0 + gyro_bias_samples: int = 0 + gyro_bias_locked: bool = False + last_debug_imu_print: float = 0.0 def capture_stick_offsets(controller: sdl2.SDL_GameController) -> Dict[int, int]: @@ -227,7 +268,9 @@ def capture_stick_offsets(controller: sdl2.SDL_GameController) -> Dict[int, int] def format_axis_offsets(offsets: Dict[int, int]) -> str: """Return a human-friendly summary of per-axis offsets (for logging).""" - return ", ".join(f"{label}={offsets.get(axis, 0):+d}" for axis, label in STICK_AXIS_LABELS) + return ", ".join( + f"{label}={offsets.get(axis, 0):+d}" for axis, label in STICK_AXIS_LABELS + ) def calibrate_axis_value(value: int, axis: int, ctx: ControllerContext) -> int: @@ -240,10 +283,63 @@ def calibrate_axis_value(value: int, axis: int, ctx: ControllerContext) -> int: return max(-32768, min(32767, value - offset)) +def clamp_int16(value: float) -> int: + return int(max(-32768.0, min(32767.0, round(value)))) + + +def convert_accel_to_raw(m_s2: float) -> int: + return clamp_int16((m_s2 / MS2_PER_G) * ACCEL_LSB_PER_G) + + +def convert_gyro_to_raw(rad_s: float, scale: float = 1.0) -> int: + return clamp_int16(rad_s * GYRO_LSB_PER_RAD_S * scale) + + +def initialize_controller_sensors(ctx: ControllerContext, console: Console) -> None: + """Enable SDL accelerometer and gyroscope on the controller if available.""" + if not all( + hasattr(sdl2, name) + for name in ( + "SDL_GameControllerHasSensor", + "SDL_GameControllerSetSensorEnabled", + "SDL_GameControllerGetSensorData", + ) + ): + ctx.sensors_supported = False + ctx.sensors_enabled = False + return + if SENSOR_ACCEL is None or SENSOR_GYRO is None: + ctx.sensors_supported = False + return + accel_ok = bool(sdl2.SDL_GameControllerHasSensor(ctx.controller, SENSOR_ACCEL)) + gyro_ok = bool(sdl2.SDL_GameControllerHasSensor(ctx.controller, SENSOR_GYRO)) + ctx.sensors_supported = accel_ok and gyro_ok + if not ctx.sensors_supported: + console.print( + f"[yellow]Controller {ctx.controller_index} has no accelerometer/gyro sensors[/yellow]" + ) + return + accel_enabled = ( + sdl2.SDL_GameControllerSetSensorEnabled(ctx.controller, SENSOR_ACCEL, SDL_TRUE) + == 0 + ) + gyro_enabled = ( + sdl2.SDL_GameControllerSetSensorEnabled(ctx.controller, SENSOR_GYRO, SDL_TRUE) + == 0 + ) + ctx.sensors_enabled = accel_enabled and gyro_enabled + if not ctx.sensors_enabled: + console.print( + f"[yellow]Controller {ctx.controller_index} failed to enable sensors[/yellow]" + ) + + class HotkeyMonitor: """Platform-aware helper that watches for configured hotkeys without blocking the main loop.""" - def __init__(self, console: Console, key_messages: Optional[Dict[str, str]] = None) -> None: + def __init__( + self, console: Console, key_messages: Optional[Dict[str, str]] = None + ) -> None: self.console = console self._platform = os.name self._fd: Optional[int] = None @@ -272,7 +368,9 @@ class HotkeyMonitor: try: import msvcrt # type: ignore except ImportError: - self.console.print("[yellow]Hotkeys disabled: msvcrt unavailable.[/yellow]") + self.console.print( + "[yellow]Hotkeys disabled: msvcrt unavailable.[/yellow]" + ) return False self._msvcrt = msvcrt self._active = True @@ -286,9 +384,10 @@ class HotkeyMonitor: import termios import tty - self._fd = sys.stdin.fileno() - self._orig_termios = termios.tcgetattr(self._fd) - tty.setcbreak(self._fd) + fd = sys.stdin.fileno() + self._fd = fd + self._orig_termios = termios.tcgetattr(fd) + tty.setcbreak(fd) self._active = True self._started = True self._print_instructions() @@ -297,7 +396,11 @@ class HotkeyMonitor: def suspend(self) -> None: if not self._active: return - if self._platform != "nt" and self._fd is not None and self._orig_termios is not None: + if ( + self._platform != "nt" + and self._fd is not None + and self._orig_termios is not None + ): import termios termios.tcsetattr(self._fd, termios.TCSADRAIN, self._orig_termios) @@ -317,7 +420,11 @@ class HotkeyMonitor: self._active = True def stop(self) -> None: - if self._platform != "nt" and self._fd is not None and self._orig_termios is not None: + if ( + self._platform != "nt" + and self._fd is not None + and self._orig_termios is not None + ): import termios termios.tcsetattr(self._fd, termios.TCSADRAIN, self._orig_termios) @@ -340,13 +447,19 @@ class HotkeyMonitor: def _print_instructions(self) -> None: if not self._keys: return - instructions = " | ".join(f"'{key.upper()}' to {message}" for key, message in self._keys.items()) + instructions = " | ".join( + f"'{key.upper()}' to {message}" for key, message in self._keys.items() + ) self.console.print(f"[magenta]Hotkeys active: {instructions}[/magenta]") def _read_key(self) -> Optional[str]: if self._platform == "nt": - if self._msvcrt and self._msvcrt.kbhit(): - ch = self._msvcrt.getwch() + if self._msvcrt is None: + return None + kbhit = getattr(self._msvcrt, "kbhit", None) + getwch = getattr(self._msvcrt, "getwch", None) + if callable(kbhit) and callable(getwch) and kbhit(): + ch = str(getwch()) if ch == "\x03": raise KeyboardInterrupt return ch @@ -362,7 +475,11 @@ class HotkeyMonitor: return ch -def zero_context_sticks(ctx: ControllerContext, console: Optional[Console] = None, reason: str = "Zeroed stick centers") -> None: +def zero_context_sticks( + ctx: ControllerContext, + console: Optional[Console] = None, + reason: str = "Zeroed stick centers", +) -> None: """Capture and store the current stick positions for a controller.""" offsets = capture_stick_offsets(ctx.controller) ctx.axis_offsets = offsets @@ -372,7 +489,9 @@ def zero_context_sticks(ctx: ControllerContext, console: Optional[Console] = Non ) -def zero_all_context_sticks(contexts: Dict[int, ControllerContext], console: Console) -> None: +def zero_all_context_sticks( + contexts: Dict[int, ControllerContext], console: Console +) -> None: """Zero every connected controller's sticks.""" if not contexts: console.print("[yellow]No controllers available to zero right now.[/yellow]") @@ -391,10 +510,14 @@ def controller_display_name(ctx: ControllerContext) -> str: return str(name) -def toggle_abxy_for_context(ctx: ControllerContext, config: BridgeConfig, console: Console) -> None: +def toggle_abxy_for_context( + ctx: ControllerContext, config: BridgeConfig, console: Console +) -> None: """Toggle the ABXY layout for a single controller.""" if config.swap_abxy_global: - console.print("[yellow]Global --swap-abxy is enabled; disable it to use per-controller toggles.[/yellow]") + console.print( + "[yellow]Global --swap-abxy is enabled; disable it to use per-controller toggles.[/yellow]" + ) return swapped = ctx.swap_abxy action = "standard" if swapped else "swapped" @@ -412,9 +535,13 @@ def prompt_swap_abxy_controller( ) -> None: """Prompt the user to choose a controller whose ABXY layout should be toggled.""" if not contexts: - console.print("[yellow]No controllers connected to toggle ABXY layout.[/yellow]") + console.print( + "[yellow]No controllers connected to toggle ABXY layout.[/yellow]" + ) return - controllers = sorted(contexts.values(), key=lambda ctx: (ctx.controller_index, ctx.instance_id)) + controllers = sorted( + contexts.values(), key=lambda ctx: (ctx.controller_index, ctx.instance_id) + ) table = Table(title="Toggle ABXY layout for a controller") table.add_column("Choice", justify="center") table.add_column("SDL Index", justify="center") @@ -459,7 +586,9 @@ def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int, str]: """Open an SDL GameController by index and return it with instance ID and GUID string.""" controller = sdl2.SDL_GameControllerOpen(index) if not controller: - raise RuntimeError(f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}") + raise RuntimeError( + f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}" + ) joystick = sdl2.SDL_GameControllerGetJoystick(controller) instance_id = sdl2.SDL_JoystickInstanceID(joystick) guid_str = guid_string_from_joystick(joystick) @@ -501,7 +630,9 @@ def open_uart_or_warn(port: str, baud: int, console: Console) -> Optional[PicoUA def build_arg_parser() -> argparse.ArgumentParser: """Construct the CLI argument parser for the bridge.""" - parser = argparse.ArgumentParser(description="Bridge SDL2 controllers to switch-pico UART (with rumble)") + parser = argparse.ArgumentParser( + description="Bridge SDL2 controllers to switch-pico UART (with rumble)" + ) parser.add_argument( "--map", action="append", @@ -514,8 +645,16 @@ def build_arg_parser() -> argparse.ArgumentParser: nargs="+", help="Serial ports to auto-pair with controllers in ascending index order.", ) - parser.add_argument("--interactive", action="store_true", help="Launch an interactive pairing UI using Rich.") - parser.add_argument("--all-ports", action="store_true", help="Include non-USB serial ports when listing devices.") + parser.add_argument( + "--interactive", + action="store_true", + help="Launch an interactive pairing UI using Rich.", + ) + parser.add_argument( + "--all-ports", + action="store_true", + help="Include non-USB serial ports when listing devices.", + ) parser.add_argument( "--frequency", type=float, @@ -622,10 +761,28 @@ def build_arg_parser() -> argparse.ArgumentParser: default=[], help="Path to an SDL2 controller mapping database (e.g. controllerdb.txt). Repeatable.", ) + parser.add_argument( + "--debug-imu", + action="store_true", + help="Print raw IMU sensor readings and converted values for debugging.", + ) + parser.add_argument( + "--no-imu", + action="store_true", + help="Disable IMU/sensor reading entirely. Useful for controllers without gyro.", + ) + parser.add_argument( + "--gyro-scale", + type=float, + default=1.0, + help="Scale factor for gyro sensitivity (default 1.0). Reduce below 1.0 if camera moves too fast.", + ) return parser -def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, SwitchButton]) -> None: +def poll_controller_buttons( + ctx: ControllerContext, button_map: Dict[int, SwitchButton] +) -> None: """Update button/hat state based on current SDL controller readings.""" changed = False for sdl_button, switch_bit in button_map.items(): @@ -665,6 +822,9 @@ class BridgeConfig: swap_abxy_indices: set[int] swap_abxy_ids: set[str] swap_abxy_global: bool + debug_imu: bool = False + no_imu: bool = False + gyro_scale: float = 1.0 @dataclass @@ -680,7 +840,9 @@ class PairingState: include_port_mfr: List[str] = field(default_factory=list) -def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, SwitchButton], Dict[int, SwitchButton], set[int]]: +def load_button_maps( + console: Console, args: argparse.Namespace +) -> Tuple[Dict[int, SwitchButton], Dict[int, SwitchButton], set[int]]: """Load SDL controller mappings and return button map variants.""" default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" if args.update_controller_db or not default_mapping.exists(): @@ -695,13 +857,19 @@ def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[i button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_B] = SwitchButton.A button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_X] = SwitchButton.Y button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_Y] = SwitchButton.X - swap_abxy_indices = {idx for idx in args.swap_abxy_index if idx is not None and idx >= 0} + swap_abxy_indices = { + idx for idx in args.swap_abxy_index if idx is not None and idx >= 0 + } for mapping_path in mappings_to_load: try: loaded = sdl2.SDL_GameControllerAddMappingsFromFile(mapping_path.encode()) - console.print(f"[green]Loaded {loaded} SDL mapping(s) from {mapping_path}[/green]") + console.print( + f"[green]Loaded {loaded} SDL mapping(s) from {mapping_path}[/green]" + ) except Exception as exc: - console.print(f"[red]Failed to load SDL mapping {mapping_path}: {exc}[/red]") + console.print( + f"[red]Failed to load SDL mapping {mapping_path}: {exc}[/red]" + ) return button_map_default, button_map_swapped, swap_abxy_indices @@ -710,7 +878,9 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon interval = 1.0 / max(args.frequency, 1.0) deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) - button_map_default, button_map_swapped, swap_abxy_indices = load_button_maps(console, args) + button_map_default, button_map_swapped, swap_abxy_indices = load_button_maps( + console, args + ) swap_abxy_guids = {g.lower() for g in args.swap_abxy_guid} return BridgeConfig( interval=interval, @@ -724,6 +894,9 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon swap_abxy_indices=swap_abxy_indices, swap_abxy_ids=set(swap_abxy_guids), # filled later once stable IDs are known swap_abxy_global=bool(args.swap_abxy), + debug_imu=bool(args.debug_imu), + no_imu=bool(args.no_imu), + gyro_scale=float(args.gyro_scale), ) @@ -734,7 +907,14 @@ def initialize_sdl(parser: argparse.ArgumentParser) -> None: set_hint("SDL_JOYSTICK_HIDAPI_SWITCH", "1") # Use controller button labels so Nintendo layouts (ABXY) map correctly on Linux. set_hint("SDL_GAMECONTROLLER_USE_BUTTON_LABELS", "1") - if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_JOYSTICK | sdl2.SDL_INIT_EVERYTHING) != 0: + if ( + sdl2.SDL_Init( + sdl2.SDL_INIT_GAMECONTROLLER + | sdl2.SDL_INIT_JOYSTICK + | sdl2.SDL_INIT_EVERYTHING + ) + != 0 + ): parser.error(f"SDL init failed: {sdl2.SDL_GetError().decode(errors='ignore')}") @@ -752,8 +932,12 @@ def detect_controllers( if sdl2.SDL_IsGameController(index): name = sdl2.SDL_GameControllerNameForIndex(index) name_str = name.decode() if isinstance(name, bytes) else str(name) - if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): - console.print(f"[yellow]Skipping controller {index} ({name_str}) due to name filter[/yellow]") + if include_controller_name and all( + substr not in name_str.lower() for substr in include_controller_name + ): + console.print( + f"[yellow]Skipping controller {index} ({name_str}) due to name filter[/yellow]" + ) continue console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") controller_indices.append(index) @@ -761,14 +945,22 @@ def detect_controllers( else: name = sdl2.SDL_JoystickNameForIndex(index) name_str = name.decode() if isinstance(name, bytes) else str(name) - if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): - console.print(f"[yellow]Skipping joystick {index} ({name_str}) due to name filter[/yellow]") + if include_controller_name and all( + substr not in name_str.lower() for substr in include_controller_name + ): + console.print( + f"[yellow]Skipping joystick {index} ({name_str}) due to name filter[/yellow]" + ) continue - console.print(f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]") + console.print( + f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]" + ) return controller_indices, controller_names -def list_controllers_with_guids(console: Console, parser: argparse.ArgumentParser) -> None: +def list_controllers_with_guids( + console: Console, parser: argparse.ArgumentParser +) -> None: """Print detected controllers with their GUID strings and exit.""" count = sdl2.SDL_NumJoysticks() if count < 0: @@ -783,10 +975,16 @@ def list_controllers_with_guids(console: Console, parser: argparse.ArgumentParse table.add_column("GUID") for idx in range(count): is_gc = sdl2.SDL_IsGameController(idx) - name = sdl2.SDL_GameControllerNameForIndex(idx) if is_gc else sdl2.SDL_JoystickNameForIndex(idx) + name = ( + sdl2.SDL_GameControllerNameForIndex(idx) + if is_gc + else sdl2.SDL_JoystickNameForIndex(idx) + ) name_str = name.decode() if isinstance(name, bytes) else str(name) guid_str = guid_string_for_device_index(idx) - table.add_row(str(idx), "GameController" if is_gc else "Joystick", name_str, guid_str) + table.add_row( + str(idx), "GameController" if is_gc else "Joystick", name_str, guid_str + ) console.print(table) @@ -825,7 +1023,9 @@ def prepare_pairing_state( elif auto_pairing_enabled: if args.ports: available_ports.extend(list(args.ports)) - console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") + console.print( + f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]" + ) else: # Passive mode: grab whatever UARTs exist now, and keep looking later. discovered = discover_serial_ports( @@ -840,7 +1040,9 @@ def prepare_pairing_state( for info in discovered: console.print(f" {info['device']} ({info['description']})") else: - console.print("[yellow]No UART devices detected yet; waiting for hotplug...[/yellow]") + console.print( + "[yellow]No UART devices detected yet; waiting for hotplug...[/yellow]" + ) mapping_by_index = {index: port for index, port in mappings} return PairingState( @@ -855,7 +1057,9 @@ def prepare_pairing_state( ) -def assign_port_for_index(pairing: PairingState, idx: int, console: Console) -> Optional[str]: +def assign_port_for_index( + pairing: PairingState, idx: int, console: Console +) -> Optional[str]: """Return the UART assigned to a controller index, auto-pairing if allowed.""" if idx in pairing.mapping_by_index: return pairing.mapping_by_index[idx] @@ -877,12 +1081,21 @@ def ports_in_use(pairing: PairingState, contexts: Dict[int, ControllerContext]) return used -def handle_removed_port(path: str, pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console) -> None: +def handle_removed_port( + path: str, + pairing: PairingState, + contexts: Dict[int, ControllerContext], + console: Console, +) -> None: """Clear mappings/contexts for a UART path that disappeared.""" if path in pairing.available_ports: pairing.available_ports.remove(path) - console.print(f"[yellow]UART {path} removed; dropping from available pool[/yellow]") - indices_to_clear = [idx for idx, mapped in pairing.mapping_by_index.items() if mapped == path] + console.print( + f"[yellow]UART {path} removed; dropping from available pool[/yellow]" + ) + indices_to_clear = [ + idx for idx, mapped in pairing.mapping_by_index.items() if mapped == path + ] for idx in indices_to_clear: pairing.mapping_by_index.pop(idx, None) pairing.auto_assigned_indices.discard(idx) @@ -900,10 +1113,14 @@ def handle_removed_port(path: str, pairing: PairingState, contexts: Dict[int, Co ctx.rumble_active = False ctx.last_rumble_energy = 0.0 ctx.last_reopen_attempt = time.monotonic() - console.print(f"[yellow]UART {path} removed; controller {ctx.controller_index} waiting for reassignment[/yellow]") + console.print( + f"[yellow]UART {path} removed; controller {ctx.controller_index} waiting for reassignment[/yellow]" + ) -def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console) -> None: +def discover_new_ports( + pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console +) -> None: """Scan for new serial ports and add unused ones to the available pool.""" if not pairing.auto_discover_ports: return @@ -927,7 +1144,9 @@ def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerCont if path in in_use or path in pairing.available_ports: continue pairing.available_ports.append(path) - console.print(f"[green]Discovered UART {path} ({info['description']}); available for pairing.[/green]") + console.print( + f"[green]Discovered UART {path} ({info['description']}); available for pairing.[/green]" + ) def pair_waiting_contexts( @@ -975,7 +1194,9 @@ def open_initial_contexts( if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): name = sdl2.SDL_JoystickNameForIndex(index) name_str = name.decode() if isinstance(name, bytes) else str(name) - console.print(f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") + console.print( + f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" + ) continue port = assign_port_for_index(pairing, index, console) if port is None and not pairing.auto_pairing_enabled: @@ -986,13 +1207,19 @@ def open_initial_contexts( console.print(f"[red]Failed to open controller {index}: {exc}[/red]") continue stable_id = guid - should_swap = index in config.swap_abxy_indices or stable_id in config.swap_abxy_ids + should_swap = ( + index in config.swap_abxy_indices or stable_id in config.swap_abxy_ids + ) uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) - console.print(f"[green]Controller {index} (id {stable_id}, inst {instance_id}) paired to {port}[/green]") + console.print( + f"[green]Controller {index} (id {stable_id}, inst {instance_id}) paired to {port}[/green]" + ) elif port: - console.print(f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]") + console.print( + f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]" + ) else: console.print( f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" @@ -1006,13 +1233,17 @@ def open_initial_contexts( uart=uart, swap_abxy=should_swap, ) + if not config.no_imu: + initialize_controller_sensors(ctx, console) if config.zero_sticks: zero_context_sticks(ctx, console) contexts[instance_id] = ctx return contexts, uarts -def handle_axis_motion(event: sdl2.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig) -> None: +def handle_axis_motion( + event: sdl2.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig +) -> None: """Process axis motion event into stick/trigger state.""" ctx = contexts.get(event.caxis.which) if not ctx: @@ -1045,6 +1276,79 @@ def handle_axis_motion(event: sdl2.SDL_Event, contexts: Dict[int, ControllerCont ctx.last_trigger_state["right"] = pressed +def handle_sensor_update( + event: sdl2.SDL_Event, + contexts: Dict[int, ControllerContext], + config: BridgeConfig, +) -> None: + """Process SDL sensor update event into IMU samples.""" + ctx = contexts.get(event.csensor.which) + if ctx is None or not ctx.sensors_enabled or config.no_imu: + return + + sensor_type = event.csensor.sensor + data = event.csensor.data + + if sensor_type == SENSOR_ACCEL: + ctx.last_accel = (float(data[0]), float(data[1]), float(data[2])) + return + + if sensor_type != SENSOR_GYRO: + return + + gx, gy, gz = float(data[0]), float(data[1]), float(data[2]) + + if not ctx.gyro_bias_locked: + if ctx.gyro_bias_samples < GYRO_BIAS_SAMPLES: + ctx.gyro_bias_x += gx + ctx.gyro_bias_y += gy + ctx.gyro_bias_z += gz + ctx.gyro_bias_samples += 1 + if ctx.gyro_bias_samples >= GYRO_BIAS_SAMPLES: + n = ctx.gyro_bias_samples + ctx.gyro_bias_x /= n + ctx.gyro_bias_y /= n + ctx.gyro_bias_z /= n + ctx.gyro_bias_locked = True + + if not ctx.gyro_bias_locked: + bx, by, bz = 0.0, 0.0, 0.0 + else: + bx, by, bz = ctx.gyro_bias_x, ctx.gyro_bias_y, ctx.gyro_bias_z + + ux, uy, uz = gx, gy, gz + ux -= bx + uy -= by + uz -= bz + + ax, ay, az = ctx.last_accel + sample = IMUSample( + accel_x=convert_accel_to_raw(ax), + accel_y=convert_accel_to_raw(ay), + accel_z=convert_accel_to_raw(az), + gyro_x=convert_gyro_to_raw(ux, config.gyro_scale), + gyro_y=convert_gyro_to_raw(uy, config.gyro_scale), + gyro_z=convert_gyro_to_raw(uz, config.gyro_scale), + ) + + ctx.imu_samples.append(sample) + if len(ctx.imu_samples) > IMU_BUFFER_SIZE: + ctx.imu_samples = ctx.imu_samples[-IMU_BUFFER_SIZE:] + + if config.debug_imu: + now = time.monotonic() + if now - ctx.last_debug_imu_print > 0.2: + ctx.last_debug_imu_print = now + print( + f"[IMU idx={ctx.controller_index}] " + f"accel_m_s2=({ax:.3f},{ay:.3f},{az:.3f}) " + f"gyro_rad_s=({gx:.3f},{gy:.3f},{gz:.3f}) " + f"bias_rad_s=({bx:.4f},{by:.4f},{bz:.4f}) " + f"raw=({sample.accel_x},{sample.accel_y},{sample.accel_z};" + f"{sample.gyro_x},{sample.gyro_y},{sample.gyro_z})" + ) + + def handle_button_event( event: sdl2.SDL_Event, config: BridgeConfig, @@ -1093,7 +1397,9 @@ def handle_device_added( if idx >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(idx): name = sdl2.SDL_JoystickNameForIndex(idx) name_str = name.decode() if isinstance(name, bytes) else str(name) - console.print(f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") + console.print( + f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" + ) return try: controller, instance_id, guid = open_controller(idx) @@ -1106,9 +1412,13 @@ def handle_device_added( uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) - console.print(f"[green]Controller {idx} (id {stable_id}, inst {instance_id}) paired to {port}[/green]") + console.print( + f"[green]Controller {idx} (id {stable_id}, inst {instance_id}) paired to {port}[/green]" + ) elif port: - console.print(f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]") + console.print( + f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]" + ) else: console.print( f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" @@ -1122,6 +1432,8 @@ def handle_device_added( uart=uart, swap_abxy=should_swap, ) + if not config.no_imu: + initialize_controller_sensors(ctx, console) if config.zero_sticks: zero_context_sticks(ctx, console) contexts[instance_id] = ctx @@ -1138,7 +1450,9 @@ def handle_device_removed( ctx = contexts.pop(instance_id, None) if not ctx: return - console.print(f"[yellow]Controller {instance_id} (id {ctx.stable_id}) removed[/yellow]") + console.print( + f"[yellow]Controller {instance_id} (id {ctx.stable_id}) removed[/yellow]" + ) if ctx.controller_index in pairing.auto_assigned_indices: # Return auto-paired UART back to the pool so a future device can use it. freed = pairing.mapping_by_index.pop(ctx.controller_index, None) @@ -1171,12 +1485,23 @@ def service_contexts( uart = open_uart_or_warn(ctx.port, args.baud, console) if uart: uarts.append(uart) - console.print(f"[green]Reconnected UART {ctx.port} for controller {ctx.controller_index}[/green]") + console.print( + f"[green]Reconnected UART {ctx.port} for controller {ctx.controller_index}[/green]" + ) ctx.uart = uart if ctx.uart is None: continue try: if now - ctx.last_send >= config.interval: + if ctx.sensors_enabled and not config.no_imu: + count = min(len(ctx.imu_samples), IMU_SAMPLES_PER_REPORT) + if count > 0: + ctx.report.imu_samples = ctx.imu_samples[:count] + ctx.imu_samples = ctx.imu_samples[count:] + else: + ctx.report.imu_samples = [] + else: + ctx.report.imu_samples = [] ctx.uart.send_report(ctx.report) ctx.last_send = now @@ -1199,7 +1524,10 @@ def service_contexts( sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) ctx.rumble_active = False ctx.last_rumble_energy = 0.0 - elif ctx.rumble_active and (now - ctx.last_rumble_change) > RUMBLE_STUCK_TIMEOUT: + elif ( + ctx.rumble_active + and (now - ctx.last_rumble_change) > RUMBLE_STUCK_TIMEOUT + ): sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) ctx.rumble_active = False ctx.last_rumble_energy = 0.0 @@ -1240,10 +1568,17 @@ def run_bridge_loop( break if event.type == sdl2.SDL_CONTROLLERAXISMOTION: handle_axis_motion(event, contexts, config) - elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): + elif event.type in ( + sdl2.SDL_CONTROLLERBUTTONDOWN, + sdl2.SDL_CONTROLLERBUTTONUP, + ): handle_button_event(event, config, contexts) + elif event.type == SDL_CONTROLLERSENSORUPDATE: + handle_sensor_update(event, contexts, config) elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: - handle_device_added(event, args, pairing, contexts, uarts, console, config) + handle_device_added( + event, args, pairing, contexts, uarts, console, config + ) elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: handle_device_removed(event, pairing, contexts, console) @@ -1289,7 +1624,9 @@ def main() -> None: list_controllers_with_guids(console, parser) return controller_indices, controller_names = detect_controllers(console, args, parser) - pairing = prepare_pairing_state(args, console, parser, controller_indices, controller_names) + pairing = prepare_pairing_state( + args, console, parser, controller_indices, controller_names + ) hotkey_messages: Dict[str, str] = {} if config.zero_hotkey: hotkey_messages[config.zero_hotkey] = "re-zero controller sticks" @@ -1299,14 +1636,20 @@ def main() -> None: hotkey_messages[config.swap_hotkey] + "; toggle ABXY layout" ) else: - hotkey_messages[config.swap_hotkey] = "toggle ABXY layout for a controller" + hotkey_messages[config.swap_hotkey] = ( + "toggle ABXY layout for a controller" + ) if hotkey_messages: candidate = HotkeyMonitor(console, hotkey_messages) if candidate.start(): hotkey_monitor = candidate - contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console, config) + contexts, uarts = open_initial_contexts( + args, pairing, controller_indices, console, config + ) if not contexts: - console.print("[yellow]No controllers opened; waiting for hotplug events...[/yellow]") + console.print( + "[yellow]No controllers opened; waiting for hotplug events...[/yellow]" + ) run_bridge_loop(args, console, config, pairing, contexts, uarts, hotkey_monitor) finally: if hotkey_monitor: From eb8994ec3bbb4417cacf415f69132f628d7aea5a Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 11:50:40 -0600 Subject: [PATCH 106/134] feat(bridge): add SDL sensor reading and gyro bias calibration - Add initialize_controller_sensors() for SDL accel/gyro enable - Add handle_sensor_update() with direct axis passthrough (no remapping) - Add gyro bias calibration (200 samples, zero gyro until locked) - Add IMU sample FIFO with pop-to-report before UART send - Add --debug-imu, --no-imu, --gyro-scale CLI flags --- .../controller_uart_bridge.py | 491 +++++++++++++++--- 1 file changed, 417 insertions(+), 74 deletions(-) diff --git a/src/switch_pico_bridge/controller_uart_bridge.py b/src/switch_pico_bridge/controller_uart_bridge.py index 4f13857..71550f6 100644 --- a/src/switch_pico_bridge/controller_uart_bridge.py +++ b/src/switch_pico_bridge/controller_uart_bridge.py @@ -34,6 +34,14 @@ from rich.table import Table from .switch_pico_uart import ( UART_BAUD, + MS2_PER_G, + RAD_TO_DEG, + ACCEL_LSB_PER_G, + GYRO_LSB_PER_RAD_S, + SENSOR_ACCEL, + SENSOR_GYRO, + IMU_SAMPLES_PER_REPORT, + IMUSample, PicoUART, SwitchButton, SwitchDpad, @@ -49,9 +57,11 @@ RUMBLE_IDLE_TIMEOUT = 0.25 # seconds without packets before forcing rumble off RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped after this RUMBLE_MIN_ACTIVE = 0.40 # below this, rumble is treated as off/noise RUMBLE_SCALE = 1.0 -CONTROLLER_DB_URL_DEFAULT = ( - "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt" -) +CONTROLLER_DB_URL_DEFAULT = "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt" +SDL_TRUE = getattr(sdl2, "SDL_TRUE", 1) +SDL_CONTROLLERSENSORUPDATE = getattr(sdl2, "SDL_CONTROLLERSENSORUPDATE", 0x658) +GYRO_BIAS_SAMPLES = 200 +IMU_BUFFER_SIZE = 32 def parse_mapping(value: str) -> Tuple[int, str]: @@ -62,7 +72,9 @@ def parse_mapping(value: str) -> Tuple[int, str]: try: idx = int(idx_str, 10) except ValueError as exc: - raise argparse.ArgumentTypeError(f"Invalid controller index '{idx_str}'") from exc + raise argparse.ArgumentTypeError( + f"Invalid controller index '{idx_str}'" + ) from exc if not port: raise argparse.ArgumentTypeError("Serial port cannot be empty") return idx, port.strip() @@ -84,9 +96,13 @@ def download_controller_db(console: Console, destination: Path, url: str) -> boo try: destination.write_bytes(data) except Exception as exc: - console.print(f"[red]Failed to write controller database to {destination}: {exc}[/red]") + console.print( + f"[red]Failed to write controller database to {destination}: {exc}[/red]" + ) return False - console.print(f"[green]Updated controller database ({len(data)} bytes) at {destination}[/green]") + console.print( + f"[green]Updated controller database ({len(data)} bytes) at {destination}[/green]" + ) return True @@ -98,7 +114,9 @@ def parse_hotkey(value: str) -> str: if not value: return "" if len(value) != 1: - raise argparse.ArgumentTypeError("Hotkeys must be a single character (or empty to disable).") + raise argparse.ArgumentTypeError( + "Hotkeys must be a single character (or empty to disable)." + ) return value @@ -151,7 +169,9 @@ def interactive_pairing( mappings: List[Tuple[int, str]] = [] for controller_idx in controller_info: if not available: - console.print("[bold red]No more UART devices available for pairing.[/bold red]") + console.print( + "[bold red]No more UART devices available for pairing.[/bold red]" + ) break table = Table( @@ -174,7 +194,9 @@ def interactive_pairing( idx = int(selection) port = available.pop(idx) mappings.append((controller_idx, port["device"])) - console.print(f"[bold green]Paired controller {controller_idx} with {port['device']}[/bold green]") + console.print( + f"[bold green]Paired controller {controller_idx} with {port['device']}[/bold green]" + ) return mappings @@ -188,7 +210,7 @@ def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> float: return 0.0 # Attenuate to feel closer to a real controller; cap at ~25% strength. scale = RUMBLE_SCALE - low = int(min(1.0, left_norm * scale) * 0xFFFF) # SDL: low_frequency_rumble + low = int(min(1.0, left_norm * scale) * 0xFFFF) # SDL: low_frequency_rumble high = int(min(1.0, right_norm * scale) * 0xFFFF) # SDL: high_frequency_rumble duration = 10 sdl2.SDL_GameControllerRumble(controller, low, high, duration) @@ -204,9 +226,18 @@ class ControllerContext: port: Optional[str] uart: Optional[PicoUART] report: SwitchReport = field(default_factory=SwitchReport) - dpad: Dict[str, bool] = field(default_factory=lambda: {"up": False, "down": False, "left": False, "right": False}) + dpad: Dict[str, bool] = field( + default_factory=lambda: { + "up": False, + "down": False, + "left": False, + "right": False, + } + ) button_state: Dict[int, bool] = field(default_factory=dict) - last_trigger_state: Dict[str, bool] = field(default_factory=lambda: {"left": False, "right": False}) + last_trigger_state: Dict[str, bool] = field( + default_factory=lambda: {"left": False, "right": False} + ) last_send: float = 0.0 last_reopen_attempt: float = 0.0 last_rumble: float = 0.0 @@ -215,6 +246,16 @@ class ControllerContext: rumble_active: bool = False axis_offsets: Dict[int, int] = field(default_factory=dict) swap_abxy: bool = False + sensors_supported: bool = False + sensors_enabled: bool = False + imu_samples: List[IMUSample] = field(default_factory=list) + last_accel: Tuple[float, float, float] = (0.0, 0.0, 0.0) + gyro_bias_x: float = 0.0 + gyro_bias_y: float = 0.0 + gyro_bias_z: float = 0.0 + gyro_bias_samples: int = 0 + gyro_bias_locked: bool = False + last_debug_imu_print: float = 0.0 def capture_stick_offsets(controller: sdl2.SDL_GameController) -> Dict[int, int]: @@ -227,7 +268,9 @@ def capture_stick_offsets(controller: sdl2.SDL_GameController) -> Dict[int, int] def format_axis_offsets(offsets: Dict[int, int]) -> str: """Return a human-friendly summary of per-axis offsets (for logging).""" - return ", ".join(f"{label}={offsets.get(axis, 0):+d}" for axis, label in STICK_AXIS_LABELS) + return ", ".join( + f"{label}={offsets.get(axis, 0):+d}" for axis, label in STICK_AXIS_LABELS + ) def calibrate_axis_value(value: int, axis: int, ctx: ControllerContext) -> int: @@ -240,10 +283,63 @@ def calibrate_axis_value(value: int, axis: int, ctx: ControllerContext) -> int: return max(-32768, min(32767, value - offset)) +def clamp_int16(value: float) -> int: + return int(max(-32768.0, min(32767.0, round(value)))) + + +def convert_accel_to_raw(m_s2: float) -> int: + return clamp_int16((m_s2 / MS2_PER_G) * ACCEL_LSB_PER_G) + + +def convert_gyro_to_raw(rad_s: float, scale: float = 1.0) -> int: + return clamp_int16(rad_s * GYRO_LSB_PER_RAD_S * scale) + + +def initialize_controller_sensors(ctx: ControllerContext, console: Console) -> None: + """Enable SDL accelerometer and gyroscope on the controller if available.""" + if not all( + hasattr(sdl2, name) + for name in ( + "SDL_GameControllerHasSensor", + "SDL_GameControllerSetSensorEnabled", + "SDL_GameControllerGetSensorData", + ) + ): + ctx.sensors_supported = False + ctx.sensors_enabled = False + return + if SENSOR_ACCEL is None or SENSOR_GYRO is None: + ctx.sensors_supported = False + return + accel_ok = bool(sdl2.SDL_GameControllerHasSensor(ctx.controller, SENSOR_ACCEL)) + gyro_ok = bool(sdl2.SDL_GameControllerHasSensor(ctx.controller, SENSOR_GYRO)) + ctx.sensors_supported = accel_ok and gyro_ok + if not ctx.sensors_supported: + console.print( + f"[yellow]Controller {ctx.controller_index} has no accelerometer/gyro sensors[/yellow]" + ) + return + accel_enabled = ( + sdl2.SDL_GameControllerSetSensorEnabled(ctx.controller, SENSOR_ACCEL, SDL_TRUE) + == 0 + ) + gyro_enabled = ( + sdl2.SDL_GameControllerSetSensorEnabled(ctx.controller, SENSOR_GYRO, SDL_TRUE) + == 0 + ) + ctx.sensors_enabled = accel_enabled and gyro_enabled + if not ctx.sensors_enabled: + console.print( + f"[yellow]Controller {ctx.controller_index} failed to enable sensors[/yellow]" + ) + + class HotkeyMonitor: """Platform-aware helper that watches for configured hotkeys without blocking the main loop.""" - def __init__(self, console: Console, key_messages: Optional[Dict[str, str]] = None) -> None: + def __init__( + self, console: Console, key_messages: Optional[Dict[str, str]] = None + ) -> None: self.console = console self._platform = os.name self._fd: Optional[int] = None @@ -272,7 +368,9 @@ class HotkeyMonitor: try: import msvcrt # type: ignore except ImportError: - self.console.print("[yellow]Hotkeys disabled: msvcrt unavailable.[/yellow]") + self.console.print( + "[yellow]Hotkeys disabled: msvcrt unavailable.[/yellow]" + ) return False self._msvcrt = msvcrt self._active = True @@ -286,9 +384,10 @@ class HotkeyMonitor: import termios import tty - self._fd = sys.stdin.fileno() - self._orig_termios = termios.tcgetattr(self._fd) - tty.setcbreak(self._fd) + fd = sys.stdin.fileno() + self._fd = fd + self._orig_termios = termios.tcgetattr(fd) + tty.setcbreak(fd) self._active = True self._started = True self._print_instructions() @@ -297,7 +396,11 @@ class HotkeyMonitor: def suspend(self) -> None: if not self._active: return - if self._platform != "nt" and self._fd is not None and self._orig_termios is not None: + if ( + self._platform != "nt" + and self._fd is not None + and self._orig_termios is not None + ): import termios termios.tcsetattr(self._fd, termios.TCSADRAIN, self._orig_termios) @@ -317,7 +420,11 @@ class HotkeyMonitor: self._active = True def stop(self) -> None: - if self._platform != "nt" and self._fd is not None and self._orig_termios is not None: + if ( + self._platform != "nt" + and self._fd is not None + and self._orig_termios is not None + ): import termios termios.tcsetattr(self._fd, termios.TCSADRAIN, self._orig_termios) @@ -340,13 +447,19 @@ class HotkeyMonitor: def _print_instructions(self) -> None: if not self._keys: return - instructions = " | ".join(f"'{key.upper()}' to {message}" for key, message in self._keys.items()) + instructions = " | ".join( + f"'{key.upper()}' to {message}" for key, message in self._keys.items() + ) self.console.print(f"[magenta]Hotkeys active: {instructions}[/magenta]") def _read_key(self) -> Optional[str]: if self._platform == "nt": - if self._msvcrt and self._msvcrt.kbhit(): - ch = self._msvcrt.getwch() + if self._msvcrt is None: + return None + kbhit = getattr(self._msvcrt, "kbhit", None) + getwch = getattr(self._msvcrt, "getwch", None) + if callable(kbhit) and callable(getwch) and kbhit(): + ch = str(getwch()) if ch == "\x03": raise KeyboardInterrupt return ch @@ -362,7 +475,11 @@ class HotkeyMonitor: return ch -def zero_context_sticks(ctx: ControllerContext, console: Optional[Console] = None, reason: str = "Zeroed stick centers") -> None: +def zero_context_sticks( + ctx: ControllerContext, + console: Optional[Console] = None, + reason: str = "Zeroed stick centers", +) -> None: """Capture and store the current stick positions for a controller.""" offsets = capture_stick_offsets(ctx.controller) ctx.axis_offsets = offsets @@ -372,7 +489,9 @@ def zero_context_sticks(ctx: ControllerContext, console: Optional[Console] = Non ) -def zero_all_context_sticks(contexts: Dict[int, ControllerContext], console: Console) -> None: +def zero_all_context_sticks( + contexts: Dict[int, ControllerContext], console: Console +) -> None: """Zero every connected controller's sticks.""" if not contexts: console.print("[yellow]No controllers available to zero right now.[/yellow]") @@ -391,10 +510,14 @@ def controller_display_name(ctx: ControllerContext) -> str: return str(name) -def toggle_abxy_for_context(ctx: ControllerContext, config: BridgeConfig, console: Console) -> None: +def toggle_abxy_for_context( + ctx: ControllerContext, config: BridgeConfig, console: Console +) -> None: """Toggle the ABXY layout for a single controller.""" if config.swap_abxy_global: - console.print("[yellow]Global --swap-abxy is enabled; disable it to use per-controller toggles.[/yellow]") + console.print( + "[yellow]Global --swap-abxy is enabled; disable it to use per-controller toggles.[/yellow]" + ) return swapped = ctx.swap_abxy action = "standard" if swapped else "swapped" @@ -412,9 +535,13 @@ def prompt_swap_abxy_controller( ) -> None: """Prompt the user to choose a controller whose ABXY layout should be toggled.""" if not contexts: - console.print("[yellow]No controllers connected to toggle ABXY layout.[/yellow]") + console.print( + "[yellow]No controllers connected to toggle ABXY layout.[/yellow]" + ) return - controllers = sorted(contexts.values(), key=lambda ctx: (ctx.controller_index, ctx.instance_id)) + controllers = sorted( + contexts.values(), key=lambda ctx: (ctx.controller_index, ctx.instance_id) + ) table = Table(title="Toggle ABXY layout for a controller") table.add_column("Choice", justify="center") table.add_column("SDL Index", justify="center") @@ -459,7 +586,9 @@ def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int, str]: """Open an SDL GameController by index and return it with instance ID and GUID string.""" controller = sdl2.SDL_GameControllerOpen(index) if not controller: - raise RuntimeError(f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}") + raise RuntimeError( + f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}" + ) joystick = sdl2.SDL_GameControllerGetJoystick(controller) instance_id = sdl2.SDL_JoystickInstanceID(joystick) guid_str = guid_string_from_joystick(joystick) @@ -501,7 +630,9 @@ def open_uart_or_warn(port: str, baud: int, console: Console) -> Optional[PicoUA def build_arg_parser() -> argparse.ArgumentParser: """Construct the CLI argument parser for the bridge.""" - parser = argparse.ArgumentParser(description="Bridge SDL2 controllers to switch-pico UART (with rumble)") + parser = argparse.ArgumentParser( + description="Bridge SDL2 controllers to switch-pico UART (with rumble)" + ) parser.add_argument( "--map", action="append", @@ -514,8 +645,16 @@ def build_arg_parser() -> argparse.ArgumentParser: nargs="+", help="Serial ports to auto-pair with controllers in ascending index order.", ) - parser.add_argument("--interactive", action="store_true", help="Launch an interactive pairing UI using Rich.") - parser.add_argument("--all-ports", action="store_true", help="Include non-USB serial ports when listing devices.") + parser.add_argument( + "--interactive", + action="store_true", + help="Launch an interactive pairing UI using Rich.", + ) + parser.add_argument( + "--all-ports", + action="store_true", + help="Include non-USB serial ports when listing devices.", + ) parser.add_argument( "--frequency", type=float, @@ -622,10 +761,28 @@ def build_arg_parser() -> argparse.ArgumentParser: default=[], help="Path to an SDL2 controller mapping database (e.g. controllerdb.txt). Repeatable.", ) + parser.add_argument( + "--debug-imu", + action="store_true", + help="Print raw IMU sensor readings and converted values for debugging.", + ) + parser.add_argument( + "--no-imu", + action="store_true", + help="Disable IMU/sensor reading entirely. Useful for controllers without gyro.", + ) + parser.add_argument( + "--gyro-scale", + type=float, + default=1.0, + help="Scale factor for gyro sensitivity (default 1.0). Reduce below 1.0 if camera moves too fast.", + ) return parser -def poll_controller_buttons(ctx: ControllerContext, button_map: Dict[int, SwitchButton]) -> None: +def poll_controller_buttons( + ctx: ControllerContext, button_map: Dict[int, SwitchButton] +) -> None: """Update button/hat state based on current SDL controller readings.""" changed = False for sdl_button, switch_bit in button_map.items(): @@ -665,6 +822,9 @@ class BridgeConfig: swap_abxy_indices: set[int] swap_abxy_ids: set[str] swap_abxy_global: bool + debug_imu: bool = False + no_imu: bool = False + gyro_scale: float = 1.0 @dataclass @@ -680,7 +840,9 @@ class PairingState: include_port_mfr: List[str] = field(default_factory=list) -def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[int, SwitchButton], Dict[int, SwitchButton], set[int]]: +def load_button_maps( + console: Console, args: argparse.Namespace +) -> Tuple[Dict[int, SwitchButton], Dict[int, SwitchButton], set[int]]: """Load SDL controller mappings and return button map variants.""" default_mapping = Path(__file__).parent / "controller_db" / "gamecontrollerdb.txt" if args.update_controller_db or not default_mapping.exists(): @@ -695,13 +857,19 @@ def load_button_maps(console: Console, args: argparse.Namespace) -> Tuple[Dict[i button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_B] = SwitchButton.A button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_X] = SwitchButton.Y button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_Y] = SwitchButton.X - swap_abxy_indices = {idx for idx in args.swap_abxy_index if idx is not None and idx >= 0} + swap_abxy_indices = { + idx for idx in args.swap_abxy_index if idx is not None and idx >= 0 + } for mapping_path in mappings_to_load: try: loaded = sdl2.SDL_GameControllerAddMappingsFromFile(mapping_path.encode()) - console.print(f"[green]Loaded {loaded} SDL mapping(s) from {mapping_path}[/green]") + console.print( + f"[green]Loaded {loaded} SDL mapping(s) from {mapping_path}[/green]" + ) except Exception as exc: - console.print(f"[red]Failed to load SDL mapping {mapping_path}: {exc}[/red]") + console.print( + f"[red]Failed to load SDL mapping {mapping_path}: {exc}[/red]" + ) return button_map_default, button_map_swapped, swap_abxy_indices @@ -710,7 +878,9 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon interval = 1.0 / max(args.frequency, 1.0) deadzone_raw = int(max(0.0, min(args.deadzone, 1.0)) * 32767) trigger_threshold = int(max(0.0, min(args.trigger_threshold, 1.0)) * 32767) - button_map_default, button_map_swapped, swap_abxy_indices = load_button_maps(console, args) + button_map_default, button_map_swapped, swap_abxy_indices = load_button_maps( + console, args + ) swap_abxy_guids = {g.lower() for g in args.swap_abxy_guid} return BridgeConfig( interval=interval, @@ -724,6 +894,9 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon swap_abxy_indices=swap_abxy_indices, swap_abxy_ids=set(swap_abxy_guids), # filled later once stable IDs are known swap_abxy_global=bool(args.swap_abxy), + debug_imu=bool(args.debug_imu), + no_imu=bool(args.no_imu), + gyro_scale=float(args.gyro_scale), ) @@ -734,7 +907,14 @@ def initialize_sdl(parser: argparse.ArgumentParser) -> None: set_hint("SDL_JOYSTICK_HIDAPI_SWITCH", "1") # Use controller button labels so Nintendo layouts (ABXY) map correctly on Linux. set_hint("SDL_GAMECONTROLLER_USE_BUTTON_LABELS", "1") - if sdl2.SDL_Init(sdl2.SDL_INIT_GAMECONTROLLER | sdl2.SDL_INIT_JOYSTICK | sdl2.SDL_INIT_EVERYTHING) != 0: + if ( + sdl2.SDL_Init( + sdl2.SDL_INIT_GAMECONTROLLER + | sdl2.SDL_INIT_JOYSTICK + | sdl2.SDL_INIT_EVERYTHING + ) + != 0 + ): parser.error(f"SDL init failed: {sdl2.SDL_GetError().decode(errors='ignore')}") @@ -752,8 +932,12 @@ def detect_controllers( if sdl2.SDL_IsGameController(index): name = sdl2.SDL_GameControllerNameForIndex(index) name_str = name.decode() if isinstance(name, bytes) else str(name) - if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): - console.print(f"[yellow]Skipping controller {index} ({name_str}) due to name filter[/yellow]") + if include_controller_name and all( + substr not in name_str.lower() for substr in include_controller_name + ): + console.print( + f"[yellow]Skipping controller {index} ({name_str}) due to name filter[/yellow]" + ) continue console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") controller_indices.append(index) @@ -761,14 +945,22 @@ def detect_controllers( else: name = sdl2.SDL_JoystickNameForIndex(index) name_str = name.decode() if isinstance(name, bytes) else str(name) - if include_controller_name and all(substr not in name_str.lower() for substr in include_controller_name): - console.print(f"[yellow]Skipping joystick {index} ({name_str}) due to name filter[/yellow]") + if include_controller_name and all( + substr not in name_str.lower() for substr in include_controller_name + ): + console.print( + f"[yellow]Skipping joystick {index} ({name_str}) due to name filter[/yellow]" + ) continue - console.print(f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]") + console.print( + f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]" + ) return controller_indices, controller_names -def list_controllers_with_guids(console: Console, parser: argparse.ArgumentParser) -> None: +def list_controllers_with_guids( + console: Console, parser: argparse.ArgumentParser +) -> None: """Print detected controllers with their GUID strings and exit.""" count = sdl2.SDL_NumJoysticks() if count < 0: @@ -783,10 +975,16 @@ def list_controllers_with_guids(console: Console, parser: argparse.ArgumentParse table.add_column("GUID") for idx in range(count): is_gc = sdl2.SDL_IsGameController(idx) - name = sdl2.SDL_GameControllerNameForIndex(idx) if is_gc else sdl2.SDL_JoystickNameForIndex(idx) + name = ( + sdl2.SDL_GameControllerNameForIndex(idx) + if is_gc + else sdl2.SDL_JoystickNameForIndex(idx) + ) name_str = name.decode() if isinstance(name, bytes) else str(name) guid_str = guid_string_for_device_index(idx) - table.add_row(str(idx), "GameController" if is_gc else "Joystick", name_str, guid_str) + table.add_row( + str(idx), "GameController" if is_gc else "Joystick", name_str, guid_str + ) console.print(table) @@ -825,7 +1023,9 @@ def prepare_pairing_state( elif auto_pairing_enabled: if args.ports: available_ports.extend(list(args.ports)) - console.print(f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]") + console.print( + f"[green]Prepared {len(available_ports)} specified UART port(s) for auto-pairing.[/green]" + ) else: # Passive mode: grab whatever UARTs exist now, and keep looking later. discovered = discover_serial_ports( @@ -840,7 +1040,9 @@ def prepare_pairing_state( for info in discovered: console.print(f" {info['device']} ({info['description']})") else: - console.print("[yellow]No UART devices detected yet; waiting for hotplug...[/yellow]") + console.print( + "[yellow]No UART devices detected yet; waiting for hotplug...[/yellow]" + ) mapping_by_index = {index: port for index, port in mappings} return PairingState( @@ -855,7 +1057,9 @@ def prepare_pairing_state( ) -def assign_port_for_index(pairing: PairingState, idx: int, console: Console) -> Optional[str]: +def assign_port_for_index( + pairing: PairingState, idx: int, console: Console +) -> Optional[str]: """Return the UART assigned to a controller index, auto-pairing if allowed.""" if idx in pairing.mapping_by_index: return pairing.mapping_by_index[idx] @@ -877,12 +1081,21 @@ def ports_in_use(pairing: PairingState, contexts: Dict[int, ControllerContext]) return used -def handle_removed_port(path: str, pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console) -> None: +def handle_removed_port( + path: str, + pairing: PairingState, + contexts: Dict[int, ControllerContext], + console: Console, +) -> None: """Clear mappings/contexts for a UART path that disappeared.""" if path in pairing.available_ports: pairing.available_ports.remove(path) - console.print(f"[yellow]UART {path} removed; dropping from available pool[/yellow]") - indices_to_clear = [idx for idx, mapped in pairing.mapping_by_index.items() if mapped == path] + console.print( + f"[yellow]UART {path} removed; dropping from available pool[/yellow]" + ) + indices_to_clear = [ + idx for idx, mapped in pairing.mapping_by_index.items() if mapped == path + ] for idx in indices_to_clear: pairing.mapping_by_index.pop(idx, None) pairing.auto_assigned_indices.discard(idx) @@ -900,10 +1113,14 @@ def handle_removed_port(path: str, pairing: PairingState, contexts: Dict[int, Co ctx.rumble_active = False ctx.last_rumble_energy = 0.0 ctx.last_reopen_attempt = time.monotonic() - console.print(f"[yellow]UART {path} removed; controller {ctx.controller_index} waiting for reassignment[/yellow]") + console.print( + f"[yellow]UART {path} removed; controller {ctx.controller_index} waiting for reassignment[/yellow]" + ) -def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console) -> None: +def discover_new_ports( + pairing: PairingState, contexts: Dict[int, ControllerContext], console: Console +) -> None: """Scan for new serial ports and add unused ones to the available pool.""" if not pairing.auto_discover_ports: return @@ -927,7 +1144,9 @@ def discover_new_ports(pairing: PairingState, contexts: Dict[int, ControllerCont if path in in_use or path in pairing.available_ports: continue pairing.available_ports.append(path) - console.print(f"[green]Discovered UART {path} ({info['description']}); available for pairing.[/green]") + console.print( + f"[green]Discovered UART {path} ({info['description']}); available for pairing.[/green]" + ) def pair_waiting_contexts( @@ -975,7 +1194,9 @@ def open_initial_contexts( if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): name = sdl2.SDL_JoystickNameForIndex(index) name_str = name.decode() if isinstance(name, bytes) else str(name) - console.print(f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") + console.print( + f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" + ) continue port = assign_port_for_index(pairing, index, console) if port is None and not pairing.auto_pairing_enabled: @@ -986,13 +1207,19 @@ def open_initial_contexts( console.print(f"[red]Failed to open controller {index}: {exc}[/red]") continue stable_id = guid - should_swap = index in config.swap_abxy_indices or stable_id in config.swap_abxy_ids + should_swap = ( + index in config.swap_abxy_indices or stable_id in config.swap_abxy_ids + ) uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) - console.print(f"[green]Controller {index} (id {stable_id}, inst {instance_id}) paired to {port}[/green]") + console.print( + f"[green]Controller {index} (id {stable_id}, inst {instance_id}) paired to {port}[/green]" + ) elif port: - console.print(f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]") + console.print( + f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]" + ) else: console.print( f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" @@ -1006,13 +1233,17 @@ def open_initial_contexts( uart=uart, swap_abxy=should_swap, ) + if not config.no_imu: + initialize_controller_sensors(ctx, console) if config.zero_sticks: zero_context_sticks(ctx, console) contexts[instance_id] = ctx return contexts, uarts -def handle_axis_motion(event: sdl2.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig) -> None: +def handle_axis_motion( + event: sdl2.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig +) -> None: """Process axis motion event into stick/trigger state.""" ctx = contexts.get(event.caxis.which) if not ctx: @@ -1045,6 +1276,79 @@ def handle_axis_motion(event: sdl2.SDL_Event, contexts: Dict[int, ControllerCont ctx.last_trigger_state["right"] = pressed +def handle_sensor_update( + event: sdl2.SDL_Event, + contexts: Dict[int, ControllerContext], + config: BridgeConfig, +) -> None: + """Process SDL sensor update event into IMU samples.""" + ctx = contexts.get(event.csensor.which) + if ctx is None or not ctx.sensors_enabled or config.no_imu: + return + + sensor_type = event.csensor.sensor + data = event.csensor.data + + if sensor_type == SENSOR_ACCEL: + ctx.last_accel = (float(data[0]), float(data[1]), float(data[2])) + return + + if sensor_type != SENSOR_GYRO: + return + + gx, gy, gz = float(data[0]), float(data[1]), float(data[2]) + + if not ctx.gyro_bias_locked: + if ctx.gyro_bias_samples < GYRO_BIAS_SAMPLES: + ctx.gyro_bias_x += gx + ctx.gyro_bias_y += gy + ctx.gyro_bias_z += gz + ctx.gyro_bias_samples += 1 + if ctx.gyro_bias_samples >= GYRO_BIAS_SAMPLES: + n = ctx.gyro_bias_samples + ctx.gyro_bias_x /= n + ctx.gyro_bias_y /= n + ctx.gyro_bias_z /= n + ctx.gyro_bias_locked = True + + if not ctx.gyro_bias_locked: + bx, by, bz = 0.0, 0.0, 0.0 + else: + bx, by, bz = ctx.gyro_bias_x, ctx.gyro_bias_y, ctx.gyro_bias_z + + ux, uy, uz = gx, gy, gz + ux -= bx + uy -= by + uz -= bz + + ax, ay, az = ctx.last_accel + sample = IMUSample( + accel_x=convert_accel_to_raw(ax), + accel_y=convert_accel_to_raw(ay), + accel_z=convert_accel_to_raw(az), + gyro_x=convert_gyro_to_raw(ux, config.gyro_scale), + gyro_y=convert_gyro_to_raw(uy, config.gyro_scale), + gyro_z=convert_gyro_to_raw(uz, config.gyro_scale), + ) + + ctx.imu_samples.append(sample) + if len(ctx.imu_samples) > IMU_BUFFER_SIZE: + ctx.imu_samples = ctx.imu_samples[-IMU_BUFFER_SIZE:] + + if config.debug_imu: + now = time.monotonic() + if now - ctx.last_debug_imu_print > 0.2: + ctx.last_debug_imu_print = now + print( + f"[IMU idx={ctx.controller_index}] " + f"accel_m_s2=({ax:.3f},{ay:.3f},{az:.3f}) " + f"gyro_rad_s=({gx:.3f},{gy:.3f},{gz:.3f}) " + f"bias_rad_s=({bx:.4f},{by:.4f},{bz:.4f}) " + f"raw=({sample.accel_x},{sample.accel_y},{sample.accel_z};" + f"{sample.gyro_x},{sample.gyro_y},{sample.gyro_z})" + ) + + def handle_button_event( event: sdl2.SDL_Event, config: BridgeConfig, @@ -1093,7 +1397,9 @@ def handle_device_added( if idx >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(idx): name = sdl2.SDL_JoystickNameForIndex(idx) name_str = name.decode() if isinstance(name, bytes) else str(name) - console.print(f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]") + console.print( + f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" + ) return try: controller, instance_id, guid = open_controller(idx) @@ -1106,9 +1412,13 @@ def handle_device_added( uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) - console.print(f"[green]Controller {idx} (id {stable_id}, inst {instance_id}) paired to {port}[/green]") + console.print( + f"[green]Controller {idx} (id {stable_id}, inst {instance_id}) paired to {port}[/green]" + ) elif port: - console.print(f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]") + console.print( + f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]" + ) else: console.print( f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" @@ -1122,6 +1432,8 @@ def handle_device_added( uart=uart, swap_abxy=should_swap, ) + if not config.no_imu: + initialize_controller_sensors(ctx, console) if config.zero_sticks: zero_context_sticks(ctx, console) contexts[instance_id] = ctx @@ -1138,7 +1450,9 @@ def handle_device_removed( ctx = contexts.pop(instance_id, None) if not ctx: return - console.print(f"[yellow]Controller {instance_id} (id {ctx.stable_id}) removed[/yellow]") + console.print( + f"[yellow]Controller {instance_id} (id {ctx.stable_id}) removed[/yellow]" + ) if ctx.controller_index in pairing.auto_assigned_indices: # Return auto-paired UART back to the pool so a future device can use it. freed = pairing.mapping_by_index.pop(ctx.controller_index, None) @@ -1171,12 +1485,23 @@ def service_contexts( uart = open_uart_or_warn(ctx.port, args.baud, console) if uart: uarts.append(uart) - console.print(f"[green]Reconnected UART {ctx.port} for controller {ctx.controller_index}[/green]") + console.print( + f"[green]Reconnected UART {ctx.port} for controller {ctx.controller_index}[/green]" + ) ctx.uart = uart if ctx.uart is None: continue try: if now - ctx.last_send >= config.interval: + if ctx.sensors_enabled and not config.no_imu: + count = min(len(ctx.imu_samples), IMU_SAMPLES_PER_REPORT) + if count > 0: + ctx.report.imu_samples = ctx.imu_samples[:count] + ctx.imu_samples = ctx.imu_samples[count:] + else: + ctx.report.imu_samples = [] + else: + ctx.report.imu_samples = [] ctx.uart.send_report(ctx.report) ctx.last_send = now @@ -1199,7 +1524,10 @@ def service_contexts( sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) ctx.rumble_active = False ctx.last_rumble_energy = 0.0 - elif ctx.rumble_active and (now - ctx.last_rumble_change) > RUMBLE_STUCK_TIMEOUT: + elif ( + ctx.rumble_active + and (now - ctx.last_rumble_change) > RUMBLE_STUCK_TIMEOUT + ): sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) ctx.rumble_active = False ctx.last_rumble_energy = 0.0 @@ -1240,10 +1568,17 @@ def run_bridge_loop( break if event.type == sdl2.SDL_CONTROLLERAXISMOTION: handle_axis_motion(event, contexts, config) - elif event.type in (sdl2.SDL_CONTROLLERBUTTONDOWN, sdl2.SDL_CONTROLLERBUTTONUP): + elif event.type in ( + sdl2.SDL_CONTROLLERBUTTONDOWN, + sdl2.SDL_CONTROLLERBUTTONUP, + ): handle_button_event(event, config, contexts) + elif event.type == SDL_CONTROLLERSENSORUPDATE: + handle_sensor_update(event, contexts, config) elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: - handle_device_added(event, args, pairing, contexts, uarts, console, config) + handle_device_added( + event, args, pairing, contexts, uarts, console, config + ) elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: handle_device_removed(event, pairing, contexts, console) @@ -1289,7 +1624,9 @@ def main() -> None: list_controllers_with_guids(console, parser) return controller_indices, controller_names = detect_controllers(console, args, parser) - pairing = prepare_pairing_state(args, console, parser, controller_indices, controller_names) + pairing = prepare_pairing_state( + args, console, parser, controller_indices, controller_names + ) hotkey_messages: Dict[str, str] = {} if config.zero_hotkey: hotkey_messages[config.zero_hotkey] = "re-zero controller sticks" @@ -1299,14 +1636,20 @@ def main() -> None: hotkey_messages[config.swap_hotkey] + "; toggle ABXY layout" ) else: - hotkey_messages[config.swap_hotkey] = "toggle ABXY layout for a controller" + hotkey_messages[config.swap_hotkey] = ( + "toggle ABXY layout for a controller" + ) if hotkey_messages: candidate = HotkeyMonitor(console, hotkey_messages) if candidate.start(): hotkey_monitor = candidate - contexts, uarts = open_initial_contexts(args, pairing, controller_indices, console, config) + contexts, uarts = open_initial_contexts( + args, pairing, controller_indices, console, config + ) if not contexts: - console.print("[yellow]No controllers opened; waiting for hotplug events...[/yellow]") + console.print( + "[yellow]No controllers opened; waiting for hotplug events...[/yellow]" + ) run_bridge_loop(args, console, config, pairing, contexts, uarts, hotkey_monitor) finally: if hotkey_monitor: From 22da7bce8fc1ca1e699f5cee2a37c91c7e4541ed Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 11:50:46 -0600 Subject: [PATCH 107/134] chore: add IMU diagnostic tool - Add tools/read_pro_imu.py from gyrov3 branch - Reads raw 0x30 HID reports from Switch Pro Controller / Pico - Lazy pyhidapi import with helpful error message when missing - Supports --list, --count, --plot, --save-prefix flags --- tools/read_pro_imu.py | 199 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100755 tools/read_pro_imu.py diff --git a/tools/read_pro_imu.py b/tools/read_pro_imu.py new file mode 100755 index 0000000..c9abdf1 --- /dev/null +++ b/tools/read_pro_imu.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +""" +Read raw IMU samples from a Nintendo Switch Pro Controller (or Pico spoof) over USB. + +Uses the `hidapi` (pyhidapi) package. Press Ctrl+C to exit. +""" + +import argparse +import struct +import sys +from typing import List, Tuple + +DEFAULT_VENDOR_ID = 0x057E +DEFAULT_PRODUCT_ID = 0x2009 # Switch Pro Controller (USB) + +try: + import hid # from pyhidapi +except ImportError: + hid = None + + +def list_devices(filter_vid=None, filter_pid=None): + devices = hid.enumerate() + for d in devices: + if filter_vid and d["vendor_id"] != filter_vid: + continue + if filter_pid and d["product_id"] != filter_pid: + continue + print( + f"VID=0x{d['vendor_id']:04X} PID=0x{d['product_id']:04X} " + f"path={d.get('path')} " + f"serial={d.get('serial_number')} " + f"manufacturer={d.get('manufacturer_string')} " + f"product={d.get('product_string')} " + f"interface={d.get('interface_number')}" + ) + return devices + + +def find_device(vendor_id: int, product_id: int): + for dev in hid.enumerate(): + if dev["vendor_id"] == vendor_id and dev["product_id"] == product_id: + return dev + return None + + +def main(): + parser = argparse.ArgumentParser( + description="Read raw 0x30 reports (IMU) from a Switch Pro Controller / Pico." + ) + parser.add_argument( + "--vid", + type=lambda x: int(x, 0), + default=DEFAULT_VENDOR_ID, + help="Vendor ID (default 0x057E)", + ) + parser.add_argument( + "--pid", + type=lambda x: int(x, 0), + default=DEFAULT_PRODUCT_ID, + help="Product ID (default 0x2009)", + ) + parser.add_argument("--path", help="Explicit HID path to open (overrides VID/PID).") + parser.add_argument( + "--count", + type=int, + default=0, + help="Stop after this many 0x30 reports (0 = infinite).", + ) + parser.add_argument( + "--timeout", type=int, default=3000, help="Read timeout ms (default 3000)." + ) + parser.add_argument( + "--list", action="store_true", help="List detected HID devices and exit." + ) + parser.add_argument( + "--plot", + action="store_true", + help="Plot accel/gyro traces after capture (requires matplotlib).", + ) + parser.add_argument( + "--save-prefix", + help="If set, save accel/gyro plots as '_accel.png' and '_gyro.png'.", + ) + args = parser.parse_args() + + if hid is None: + print( + "pyhidapi is required for this tool. Install it with: pip install pyhidapi", + file=sys.stderr, + ) + sys.exit(1) + + if args.list: + list_devices() + return + + if args.path: + dev_info = { + "path": bytes(args.path, encoding="utf-8"), + "vendor_id": args.vid, + "product_id": args.pid, + } + else: + dev_info = find_device(args.vid, args.pid) + if not dev_info: + print( + f"No HID device found for VID=0x{args.vid:04X} PID=0x{args.pid:04X}. " + "Use --list to inspect devices or --path to target a specific one.", + file=sys.stderr, + ) + sys.exit(1) + + device = hid.device() + device.open_path(dev_info["path"]) + device.set_nonblocking(False) + print( + f"Reading raw 0x30 reports from device (VID=0x{args.vid:04X} PID=0x{args.pid:04X})... " + "Ctrl+C to stop." + ) + accel_series: List[Tuple[int, int, int]] = [] + gyro_series: List[Tuple[int, int, int]] = [] + try: + read_count = 0 + while args.count == 0 or read_count < args.count: + data = device.read(64, timeout_ms=args.timeout) + if not data: + print(f"(timeout after {args.timeout} ms, no data)") + continue + if data[0] != 0x30: + print(f"(non-0x30 report id=0x{data[0]:02X}, len={len(data)})") + continue + samples = [] + offset = 13 # accel_x starts at byte 13 + for _ in range(3): + ax, ay, az, gx, gy, gz = struct.unpack_from( + " Date: Mon, 16 Mar 2026 11:50:46 -0600 Subject: [PATCH 108/134] chore: add IMU diagnostic tool - Add tools/read_pro_imu.py from gyrov3 branch - Reads raw 0x30 HID reports from Switch Pro Controller / Pico - Lazy pyhidapi import with helpful error message when missing - Supports --list, --count, --plot, --save-prefix flags --- tools/read_pro_imu.py | 199 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100755 tools/read_pro_imu.py diff --git a/tools/read_pro_imu.py b/tools/read_pro_imu.py new file mode 100755 index 0000000..c9abdf1 --- /dev/null +++ b/tools/read_pro_imu.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +""" +Read raw IMU samples from a Nintendo Switch Pro Controller (or Pico spoof) over USB. + +Uses the `hidapi` (pyhidapi) package. Press Ctrl+C to exit. +""" + +import argparse +import struct +import sys +from typing import List, Tuple + +DEFAULT_VENDOR_ID = 0x057E +DEFAULT_PRODUCT_ID = 0x2009 # Switch Pro Controller (USB) + +try: + import hid # from pyhidapi +except ImportError: + hid = None + + +def list_devices(filter_vid=None, filter_pid=None): + devices = hid.enumerate() + for d in devices: + if filter_vid and d["vendor_id"] != filter_vid: + continue + if filter_pid and d["product_id"] != filter_pid: + continue + print( + f"VID=0x{d['vendor_id']:04X} PID=0x{d['product_id']:04X} " + f"path={d.get('path')} " + f"serial={d.get('serial_number')} " + f"manufacturer={d.get('manufacturer_string')} " + f"product={d.get('product_string')} " + f"interface={d.get('interface_number')}" + ) + return devices + + +def find_device(vendor_id: int, product_id: int): + for dev in hid.enumerate(): + if dev["vendor_id"] == vendor_id and dev["product_id"] == product_id: + return dev + return None + + +def main(): + parser = argparse.ArgumentParser( + description="Read raw 0x30 reports (IMU) from a Switch Pro Controller / Pico." + ) + parser.add_argument( + "--vid", + type=lambda x: int(x, 0), + default=DEFAULT_VENDOR_ID, + help="Vendor ID (default 0x057E)", + ) + parser.add_argument( + "--pid", + type=lambda x: int(x, 0), + default=DEFAULT_PRODUCT_ID, + help="Product ID (default 0x2009)", + ) + parser.add_argument("--path", help="Explicit HID path to open (overrides VID/PID).") + parser.add_argument( + "--count", + type=int, + default=0, + help="Stop after this many 0x30 reports (0 = infinite).", + ) + parser.add_argument( + "--timeout", type=int, default=3000, help="Read timeout ms (default 3000)." + ) + parser.add_argument( + "--list", action="store_true", help="List detected HID devices and exit." + ) + parser.add_argument( + "--plot", + action="store_true", + help="Plot accel/gyro traces after capture (requires matplotlib).", + ) + parser.add_argument( + "--save-prefix", + help="If set, save accel/gyro plots as '_accel.png' and '_gyro.png'.", + ) + args = parser.parse_args() + + if hid is None: + print( + "pyhidapi is required for this tool. Install it with: pip install pyhidapi", + file=sys.stderr, + ) + sys.exit(1) + + if args.list: + list_devices() + return + + if args.path: + dev_info = { + "path": bytes(args.path, encoding="utf-8"), + "vendor_id": args.vid, + "product_id": args.pid, + } + else: + dev_info = find_device(args.vid, args.pid) + if not dev_info: + print( + f"No HID device found for VID=0x{args.vid:04X} PID=0x{args.pid:04X}. " + "Use --list to inspect devices or --path to target a specific one.", + file=sys.stderr, + ) + sys.exit(1) + + device = hid.device() + device.open_path(dev_info["path"]) + device.set_nonblocking(False) + print( + f"Reading raw 0x30 reports from device (VID=0x{args.vid:04X} PID=0x{args.pid:04X})... " + "Ctrl+C to stop." + ) + accel_series: List[Tuple[int, int, int]] = [] + gyro_series: List[Tuple[int, int, int]] = [] + try: + read_count = 0 + while args.count == 0 or read_count < args.count: + data = device.read(64, timeout_ms=args.timeout) + if not data: + print(f"(timeout after {args.timeout} ms, no data)") + continue + if data[0] != 0x30: + print(f"(non-0x30 report id=0x{data[0]:02X}, len={len(data)})") + continue + samples = [] + offset = 13 # accel_x starts at byte 13 + for _ in range(3): + ax, ay, az, gx, gy, gz = struct.unpack_from( + " Date: Mon, 16 Mar 2026 12:00:44 -0600 Subject: [PATCH 109/134] fix(bridge): reverse SDL axis remapping to recover Nintendo-native IMU axes SDL's hidapi_switch.c SendSensorUpdate() remaps Nintendo's raw sensor axes to match PlayStation convention before emitting SDL sensor events: SDL_X = -Nintendo_Y, SDL_Y = +Nintendo_Z, SDL_Z = -Nintendo_X This commit reverses that transform so the values sent to the Pico are in Nintendo's native coordinate frame: Nintendo_X_raw = -SDL_Z Nintendo_Y_raw = -SDL_X Nintendo_Z_raw = +SDL_Y Same mapping for gyro (bias subtraction happens before axis reversal). Confirmed from SDL3 hidapi_switch.c source (SDL2 uses same SDL3 backend). --- .../controller_uart_bridge.py | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/switch_pico_bridge/controller_uart_bridge.py b/src/switch_pico_bridge/controller_uart_bridge.py index 71550f6..7b280d3 100644 --- a/src/switch_pico_bridge/controller_uart_bridge.py +++ b/src/switch_pico_bridge/controller_uart_bridge.py @@ -1322,13 +1322,22 @@ def handle_sensor_update( uz -= bz ax, ay, az = ctx.last_accel + # SDL (hidapi_switch.c SendSensorUpdate) remaps Nintendo's native axes to match + # PlayStation convention before emitting sensor events: + # SDL_out[0] (X) = -(Nintendo_Y * scale) + # SDL_out[1] (Y) = +(Nintendo_Z * scale) + # SDL_out[2] (Z) = -(Nintendo_X * scale) + # We must reverse that to recover Nintendo-native values: + # Nintendo_X = -SDL_Z + # Nintendo_Y = -SDL_X + # Nintendo_Z = +SDL_Y sample = IMUSample( - accel_x=convert_accel_to_raw(ax), - accel_y=convert_accel_to_raw(ay), - accel_z=convert_accel_to_raw(az), - gyro_x=convert_gyro_to_raw(ux, config.gyro_scale), - gyro_y=convert_gyro_to_raw(uy, config.gyro_scale), - gyro_z=convert_gyro_to_raw(uz, config.gyro_scale), + accel_x=convert_accel_to_raw(-az), + accel_y=convert_accel_to_raw(-ax), + accel_z=convert_accel_to_raw(ay), + gyro_x=convert_gyro_to_raw(-uz, config.gyro_scale), + gyro_y=convert_gyro_to_raw(-ux, config.gyro_scale), + gyro_z=convert_gyro_to_raw(uy, config.gyro_scale), ) ctx.imu_samples.append(sample) From a55b3786c152c5fd5b868b4add16d36c0425ea7f Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 12:00:44 -0600 Subject: [PATCH 110/134] fix(bridge): reverse SDL axis remapping to recover Nintendo-native IMU axes SDL's hidapi_switch.c SendSensorUpdate() remaps Nintendo's raw sensor axes to match PlayStation convention before emitting SDL sensor events: SDL_X = -Nintendo_Y, SDL_Y = +Nintendo_Z, SDL_Z = -Nintendo_X This commit reverses that transform so the values sent to the Pico are in Nintendo's native coordinate frame: Nintendo_X_raw = -SDL_Z Nintendo_Y_raw = -SDL_X Nintendo_Z_raw = +SDL_Y Same mapping for gyro (bias subtraction happens before axis reversal). Confirmed from SDL3 hidapi_switch.c source (SDL2 uses same SDL3 backend). --- .../controller_uart_bridge.py | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/switch_pico_bridge/controller_uart_bridge.py b/src/switch_pico_bridge/controller_uart_bridge.py index 71550f6..7b280d3 100644 --- a/src/switch_pico_bridge/controller_uart_bridge.py +++ b/src/switch_pico_bridge/controller_uart_bridge.py @@ -1322,13 +1322,22 @@ def handle_sensor_update( uz -= bz ax, ay, az = ctx.last_accel + # SDL (hidapi_switch.c SendSensorUpdate) remaps Nintendo's native axes to match + # PlayStation convention before emitting sensor events: + # SDL_out[0] (X) = -(Nintendo_Y * scale) + # SDL_out[1] (Y) = +(Nintendo_Z * scale) + # SDL_out[2] (Z) = -(Nintendo_X * scale) + # We must reverse that to recover Nintendo-native values: + # Nintendo_X = -SDL_Z + # Nintendo_Y = -SDL_X + # Nintendo_Z = +SDL_Y sample = IMUSample( - accel_x=convert_accel_to_raw(ax), - accel_y=convert_accel_to_raw(ay), - accel_z=convert_accel_to_raw(az), - gyro_x=convert_gyro_to_raw(ux, config.gyro_scale), - gyro_y=convert_gyro_to_raw(uy, config.gyro_scale), - gyro_z=convert_gyro_to_raw(uz, config.gyro_scale), + accel_x=convert_accel_to_raw(-az), + accel_y=convert_accel_to_raw(-ax), + accel_z=convert_accel_to_raw(ay), + gyro_x=convert_gyro_to_raw(-uz, config.gyro_scale), + gyro_y=convert_gyro_to_raw(-ux, config.gyro_scale), + gyro_z=convert_gyro_to_raw(uy, config.gyro_scale), ) ctx.imu_samples.append(sample) From 91c619691d633558e42ec8085cf6805d8215f76e Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 12:00:49 -0600 Subject: [PATCH 111/134] chore: update README with IMU/motion controls documentation --- README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e64a169..01b46e6 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,8 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a 5. Connect the Pico to the Switch (dock USB-A or USB-C OTG); the Switch should see it as a wired Pro Controller. ## Planned features -- IMU support for motion controls (gyro/accelerometer passthrough for controllers that support it to the Switch) (WIP branch on `gyrov3`). ## Limitations -- No motion controls/IMU passthrough yet (planned). - No NFC/amiibo/IR support. - Rumble is best-effort: it depends on the Switch sending rumble and SDL2 being able to drive haptics on your specific controller. - Requires a host computer running the bridge; the Pico is not a Bluetooth/USB host for controllers. @@ -188,6 +186,9 @@ Options: - `--swap-abxy-guid GUID` (repeatable) to flip AB/XY for a specific physical controller (GUID is stable across runs). - `--swap-hotkey x` to pick the runtime hotkey that prompts you to toggle ABXY layout for a specific connected controller (default `x`; empty string disables). - `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `switch_pico_bridge/controller_db/gamecontrollerdb.txt`). +- `--debug-imu` to print raw gyroscope and accelerometer readings every ~200ms (useful for verifying sensor data and troubleshooting). +- `--no-imu` to disable sensor reading entirely (useful for controllers without gyro, or if motion causes issues). +- `--gyro-scale FLOAT` to adjust gyroscope sensitivity (default 1.0; reduce below 1.0 if camera rotates too fast; increase above 1.0 for more sensitivity). ### Runtime hotkeys - By default, pressing `z` in the terminal re-samples every connected controller's sticks and re-applies neutral offsets. Change/disable with `--zero-hotkey`. @@ -228,6 +229,28 @@ with SwitchUARTClient("/dev/cu.usbserial-0001") as client: ### Linux tips - You may need udev permissions for `/dev/ttyUSB*`/`/dev/ttyACM*` (add user to `dialout`/`uucp` or use `udev` rules). +## IMU / Motion Controls + +The bridge supports gyroscope and accelerometer passthrough from controllers that have motion sensors (e.g. the Nintendo Switch Pro Controller, DualSense). Motion data is forwarded to the Pico which injects it into the emulated Switch Pro Controller's HID reports. + +### Requirements +- A controller with gyro/accelerometer support (SDL2 must be able to enable sensors on it). +- The Switch will automatically use motion data once the controller is recognised as a Pro Controller. + +### Gyro bias calibration +On startup, the bridge collects the first 200 gyro readings while the controller is stationary and averages them to compute a per-axis bias (zero-rate offset). Gyro output is zeroed during this ~1 second calibration window, then bias is subtracted from all subsequent readings. Keep the controller still when starting the bridge for best results. Use `--no-gyro-bias` to skip calibration and use raw values directly. + +### CLI flags +- `--debug-imu`: Print raw sensor values (m/s² and rad/s) and converted Switch integer counts every ~200ms. Useful for verifying the sensor is detected and producing sensible data. +- `--no-imu`: Disable IMU entirely. The bridge sends zero motion data to the Pico, which sends zero-filled IMU bytes to the Switch. Buttons and sticks are unaffected. +- `--gyro-scale FLOAT` (default 1.0): Multiply all gyro values by this factor before sending. Reduce below 1.0 if the camera moves too fast; increase above 1.0 for more sensitivity. + +### Troubleshooting +- **Gyro not detected**: Run with `--debug-imu`. If no IMU readings appear, SDL2 cannot see sensors on your controller (may not be supported or driver issue). On Linux, the `hid-nintendo` kernel driver routes Pro Controller IMU to a separate evdev device that SDL2 cannot read; use Windows or macOS for gyro passthrough. +- **Wild camera swinging**: Start with `--gyro-scale 0.3` and increase gradually. Ensure the controller is still during the first second of startup (bias calibration). +- **Verifying Pico output**: Use `python tools/read_pro_imu.py --vid 0x057E --pid 0x2009` to read raw IMU bytes directly from the Pico's USB HID output and confirm non-zero values appear. +- **SDL2 accuracy**: SDL2 (version < 2.32.7) has a known inaccuracy bug with Switch Pro Controller gyro data. Updating the SDL2 shared library to 2.32.7 or later improves accuracy. + ## References - GP2040-CE (controller firmware ecosystem): https://github.com/OpenStickCommunity/GP2040-CE - nxbt (Switch controller research/tools): https://github.com/Brikwerk/nxbt From 424c8feb575a2c93ef7757323f95fd0ba4e916b2 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 12:00:49 -0600 Subject: [PATCH 112/134] chore: update README with IMU/motion controls documentation --- README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e64a169..01b46e6 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,8 @@ Raspberry Pi Pico firmware that emulates a Switch Pro controller over USB and a 5. Connect the Pico to the Switch (dock USB-A or USB-C OTG); the Switch should see it as a wired Pro Controller. ## Planned features -- IMU support for motion controls (gyro/accelerometer passthrough for controllers that support it to the Switch) (WIP branch on `gyrov3`). ## Limitations -- No motion controls/IMU passthrough yet (planned). - No NFC/amiibo/IR support. - Rumble is best-effort: it depends on the Switch sending rumble and SDL2 being able to drive haptics on your specific controller. - Requires a host computer running the bridge; the Pico is not a Bluetooth/USB host for controllers. @@ -188,6 +186,9 @@ Options: - `--swap-abxy-guid GUID` (repeatable) to flip AB/XY for a specific physical controller (GUID is stable across runs). - `--swap-hotkey x` to pick the runtime hotkey that prompts you to toggle ABXY layout for a specific connected controller (default `x`; empty string disables). - `--sdl-mapping path/to/gamecontrollerdb.txt` to load extra SDL mappings (defaults to `switch_pico_bridge/controller_db/gamecontrollerdb.txt`). +- `--debug-imu` to print raw gyroscope and accelerometer readings every ~200ms (useful for verifying sensor data and troubleshooting). +- `--no-imu` to disable sensor reading entirely (useful for controllers without gyro, or if motion causes issues). +- `--gyro-scale FLOAT` to adjust gyroscope sensitivity (default 1.0; reduce below 1.0 if camera rotates too fast; increase above 1.0 for more sensitivity). ### Runtime hotkeys - By default, pressing `z` in the terminal re-samples every connected controller's sticks and re-applies neutral offsets. Change/disable with `--zero-hotkey`. @@ -228,6 +229,28 @@ with SwitchUARTClient("/dev/cu.usbserial-0001") as client: ### Linux tips - You may need udev permissions for `/dev/ttyUSB*`/`/dev/ttyACM*` (add user to `dialout`/`uucp` or use `udev` rules). +## IMU / Motion Controls + +The bridge supports gyroscope and accelerometer passthrough from controllers that have motion sensors (e.g. the Nintendo Switch Pro Controller, DualSense). Motion data is forwarded to the Pico which injects it into the emulated Switch Pro Controller's HID reports. + +### Requirements +- A controller with gyro/accelerometer support (SDL2 must be able to enable sensors on it). +- The Switch will automatically use motion data once the controller is recognised as a Pro Controller. + +### Gyro bias calibration +On startup, the bridge collects the first 200 gyro readings while the controller is stationary and averages them to compute a per-axis bias (zero-rate offset). Gyro output is zeroed during this ~1 second calibration window, then bias is subtracted from all subsequent readings. Keep the controller still when starting the bridge for best results. Use `--no-gyro-bias` to skip calibration and use raw values directly. + +### CLI flags +- `--debug-imu`: Print raw sensor values (m/s² and rad/s) and converted Switch integer counts every ~200ms. Useful for verifying the sensor is detected and producing sensible data. +- `--no-imu`: Disable IMU entirely. The bridge sends zero motion data to the Pico, which sends zero-filled IMU bytes to the Switch. Buttons and sticks are unaffected. +- `--gyro-scale FLOAT` (default 1.0): Multiply all gyro values by this factor before sending. Reduce below 1.0 if the camera moves too fast; increase above 1.0 for more sensitivity. + +### Troubleshooting +- **Gyro not detected**: Run with `--debug-imu`. If no IMU readings appear, SDL2 cannot see sensors on your controller (may not be supported or driver issue). On Linux, the `hid-nintendo` kernel driver routes Pro Controller IMU to a separate evdev device that SDL2 cannot read; use Windows or macOS for gyro passthrough. +- **Wild camera swinging**: Start with `--gyro-scale 0.3` and increase gradually. Ensure the controller is still during the first second of startup (bias calibration). +- **Verifying Pico output**: Use `python tools/read_pro_imu.py --vid 0x057E --pid 0x2009` to read raw IMU bytes directly from the Pico's USB HID output and confirm non-zero values appear. +- **SDL2 accuracy**: SDL2 (version < 2.32.7) has a known inaccuracy bug with Switch Pro Controller gyro data. Updating the SDL2 shared library to 2.32.7 or later improves accuracy. + ## References - GP2040-CE (controller firmware ecosystem): https://github.com/OpenStickCommunity/GP2040-CE - nxbt (Switch controller research/tools): https://github.com/Brikwerk/nxbt From e13ba506cfc31fa8f02f50a9ac2a445d1ecf246b Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 12:19:36 -0600 Subject: [PATCH 113/134] chore: add uv lockfile for reproducible dependency resolution --- uv.lock | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 uv.lock diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..55858e0 --- /dev/null +++ b/uv.lock @@ -0,0 +1,104 @@ +version = 1 +revision = 3 +requires-python = ">=3.9" +resolution-markers = [ + "python_full_version >= '3.10'", + "python_full_version < '3.10'", +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "mdurl", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +dependencies = [ + { name = "mdurl", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pysdl2" +version = "0.9.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/ff/8704d84ad4d25f0a7bf7912504f64575e432e8d57dfba2fe35f5b2db7e04/pysdl2-0.9.17.tar.gz", hash = "sha256:48c6ef01a4eb123db5f7e46e1a1b565675755b07e615f3fe20a623c94735b52b", size = 775955, upload-time = "2024-12-30T18:07:27.562Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/e2/399ea7900e7510096aeb41439e6f1540bef0bdf7e15cc2d8464e4adb71e8/PySDL2-0.9.17-py3-none-any.whl", hash = "sha256:fe923dbf5c7b27bbc1eb2bf58abfa793f8f13fd7ae8b27b1bc2de49920bcbd41", size = 583137, upload-time = "2024-12-30T18:07:25.987Z" }, +] + +[[package]] +name = "pyserial" +version = "3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload-time = "2020-11-23T03:59:15.045Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" }, +] + +[[package]] +name = "rich" +version = "14.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, +] + +[[package]] +name = "switch-pico-bridge" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "pysdl2" }, + { name = "pyserial" }, + { name = "rich" }, +] + +[package.metadata] +requires-dist = [ + { name = "pysdl2" }, + { name = "pyserial" }, + { name = "rich" }, +] From 1f016a0848e8ec614a220a6efaf6a4cdd65e9fe6 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 12:19:36 -0600 Subject: [PATCH 114/134] chore: add uv lockfile for reproducible dependency resolution --- uv.lock | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 uv.lock diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..55858e0 --- /dev/null +++ b/uv.lock @@ -0,0 +1,104 @@ +version = 1 +revision = 3 +requires-python = ">=3.9" +resolution-markers = [ + "python_full_version >= '3.10'", + "python_full_version < '3.10'", +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "mdurl", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +dependencies = [ + { name = "mdurl", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pysdl2" +version = "0.9.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/ff/8704d84ad4d25f0a7bf7912504f64575e432e8d57dfba2fe35f5b2db7e04/pysdl2-0.9.17.tar.gz", hash = "sha256:48c6ef01a4eb123db5f7e46e1a1b565675755b07e615f3fe20a623c94735b52b", size = 775955, upload-time = "2024-12-30T18:07:27.562Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/e2/399ea7900e7510096aeb41439e6f1540bef0bdf7e15cc2d8464e4adb71e8/PySDL2-0.9.17-py3-none-any.whl", hash = "sha256:fe923dbf5c7b27bbc1eb2bf58abfa793f8f13fd7ae8b27b1bc2de49920bcbd41", size = 583137, upload-time = "2024-12-30T18:07:25.987Z" }, +] + +[[package]] +name = "pyserial" +version = "3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload-time = "2020-11-23T03:59:15.045Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" }, +] + +[[package]] +name = "rich" +version = "14.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, +] + +[[package]] +name = "switch-pico-bridge" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "pysdl2" }, + { name = "pyserial" }, + { name = "rich" }, +] + +[package.metadata] +requires-dist = [ + { name = "pysdl2" }, + { name = "pyserial" }, + { name = "rich" }, +] From 3f6bf3dee291e61e53ade654327a30b30f3ad3b6 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 16:54:45 -0600 Subject: [PATCH 115/134] chore: replace pysdl2 with PySDL3 for SDL3 sensor support SDL3 includes a dedicated 8BitDo HIDAPI driver with native gyro/accel sensor support, which SDL2 lacked. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- pyproject.toml | 2 +- uv.lock | 1088 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 1082 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2fcf4e5..9f8a3df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ requires-python = ">=3.9" authors = [{ name = "Switch Pico Maintainers" }] dependencies = [ "pyserial", - "pysdl2", + "PySDL3", "rich", ] diff --git a/uv.lock b/uv.lock index 55858e0..c8f27d1 100644 --- a/uv.lock +++ b/uv.lock @@ -1,11 +1,465 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.9" resolution-markers = [ "python_full_version >= '3.10'", "python_full_version < '3.10'", ] +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl", version = "1.22.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "yarl", version = "1.23.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950, upload-time = "2026-01-03T17:29:13.002Z" }, + { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099, upload-time = "2026-01-03T17:29:15.268Z" }, + { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072, upload-time = "2026-01-03T17:29:16.922Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588, upload-time = "2026-01-03T17:29:18.539Z" }, + { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334, upload-time = "2026-01-03T17:29:21.028Z" }, + { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656, upload-time = "2026-01-03T17:29:22.531Z" }, + { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625, upload-time = "2026-01-03T17:29:24.276Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604, upload-time = "2026-01-03T17:29:26.099Z" }, + { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370, upload-time = "2026-01-03T17:29:28.121Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023, upload-time = "2026-01-03T17:29:30.002Z" }, + { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680, upload-time = "2026-01-03T17:29:31.782Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407, upload-time = "2026-01-03T17:29:33.392Z" }, + { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047, upload-time = "2026-01-03T17:29:34.855Z" }, + { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264, upload-time = "2026-01-03T17:29:36.389Z" }, + { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275, upload-time = "2026-01-03T17:29:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053, upload-time = "2026-01-03T17:29:40.074Z" }, + { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687, upload-time = "2026-01-03T17:29:41.819Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4c/a164164834f03924d9a29dc3acd9e7ee58f95857e0b467f6d04298594ebb/aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b", size = 746051, upload-time = "2026-01-03T17:29:43.287Z" }, + { url = "https://files.pythonhosted.org/packages/82/71/d5c31390d18d4f58115037c432b7e0348c60f6f53b727cad33172144a112/aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64", size = 499234, upload-time = "2026-01-03T17:29:44.822Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c9/741f8ac91e14b1d2e7100690425a5b2b919a87a5075406582991fb7de920/aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea", size = 494979, upload-time = "2026-01-03T17:29:46.405Z" }, + { url = "https://files.pythonhosted.org/packages/75/b5/31d4d2e802dfd59f74ed47eba48869c1c21552c586d5e81a9d0d5c2ad640/aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a", size = 1748297, upload-time = "2026-01-03T17:29:48.083Z" }, + { url = "https://files.pythonhosted.org/packages/1a/3e/eefad0ad42959f226bb79664826883f2687d602a9ae2941a18e0484a74d3/aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540", size = 1707172, upload-time = "2026-01-03T17:29:49.648Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3a/54a64299fac2891c346cdcf2aa6803f994a2e4beeaf2e5a09dcc54acc842/aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b", size = 1805405, upload-time = "2026-01-03T17:29:51.244Z" }, + { url = "https://files.pythonhosted.org/packages/6c/70/ddc1b7169cf64075e864f64595a14b147a895a868394a48f6a8031979038/aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3", size = 1899449, upload-time = "2026-01-03T17:29:53.938Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7e/6815aab7d3a56610891c76ef79095677b8b5be6646aaf00f69b221765021/aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1", size = 1748444, upload-time = "2026-01-03T17:29:55.484Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f2/073b145c4100da5511f457dc0f7558e99b2987cf72600d42b559db856fbc/aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3", size = 1606038, upload-time = "2026-01-03T17:29:57.179Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c1/778d011920cae03ae01424ec202c513dc69243cf2db303965615b81deeea/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440", size = 1724156, upload-time = "2026-01-03T17:29:58.914Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cb/3419eabf4ec1e9ec6f242c32b689248365a1cf621891f6f0386632525494/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7", size = 1722340, upload-time = "2026-01-03T17:30:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/7a/e5/76cf77bdbc435bf233c1f114edad39ed4177ccbfab7c329482b179cff4f4/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c", size = 1783041, upload-time = "2026-01-03T17:30:03.609Z" }, + { url = "https://files.pythonhosted.org/packages/9d/d4/dd1ca234c794fd29c057ce8c0566b8ef7fd6a51069de5f06fa84b9a1971c/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51", size = 1596024, upload-time = "2026-01-03T17:30:05.132Z" }, + { url = "https://files.pythonhosted.org/packages/55/58/4345b5f26661a6180afa686c473620c30a66afdf120ed3dd545bbc809e85/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4", size = 1804590, upload-time = "2026-01-03T17:30:07.135Z" }, + { url = "https://files.pythonhosted.org/packages/7b/06/05950619af6c2df7e0a431d889ba2813c9f0129cec76f663e547a5ad56f2/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29", size = 1740355, upload-time = "2026-01-03T17:30:09.083Z" }, + { url = "https://files.pythonhosted.org/packages/3e/80/958f16de79ba0422d7c1e284b2abd0c84bc03394fbe631d0a39ffa10e1eb/aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239", size = 433701, upload-time = "2026-01-03T17:30:10.869Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f2/27cdf04c9851712d6c1b99df6821a6623c3c9e55956d4b1e318c337b5a48/aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f", size = 457678, upload-time = "2026-01-03T17:30:12.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, + { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, + { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, + { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, + { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, + { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, + { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, + { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, + { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, + { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, + { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, + { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, + { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, + { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, + { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, + { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, + { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, + { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, + { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, + { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, + { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, + { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, + { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, + { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, + { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, + { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, + { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, + { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, + { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, + { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, + { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, + { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, + { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, + { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, + { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, + { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, + { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, + { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, + { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, + { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, + { url = "https://files.pythonhosted.org/packages/bf/79/446655656861d3e7e2c32bfcf160c7aa9e9dc63776a691b124dba65cdd77/aiohttp-3.13.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31a83ea4aead760dfcb6962efb1d861db48c34379f2ff72db9ddddd4cda9ea2e", size = 741433, upload-time = "2026-01-03T17:32:26.453Z" }, + { url = "https://files.pythonhosted.org/packages/cb/49/773c4b310b5140d2fb5e79bb0bf40b7b41dad80a288ca1a8759f5f72bda9/aiohttp-3.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:988a8c5e317544fdf0d39871559e67b6341065b87fceac641108c2096d5506b7", size = 497332, upload-time = "2026-01-03T17:32:28.37Z" }, + { url = "https://files.pythonhosted.org/packages/bc/31/1dcbc4b83a4e6f76a0ad883f07f21ffbfe29750c89db97381701508c9f45/aiohttp-3.13.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b174f267b5cfb9a7dba9ee6859cecd234e9a681841eb85068059bc867fb8f02", size = 492365, upload-time = "2026-01-03T17:32:30.234Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b5/b50657496c8754482cd7964e50aaf3aa84b3db61ed45daec4c1aec5b94b4/aiohttp-3.13.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:947c26539750deeaee933b000fb6517cc770bbd064bad6033f1cff4803881e43", size = 1660440, upload-time = "2026-01-03T17:32:32.586Z" }, + { url = "https://files.pythonhosted.org/packages/2a/73/9b69e5139d89d75127569298931444ad78ea86a5befd5599780b1e9a6880/aiohttp-3.13.3-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9ebf57d09e131f5323464bd347135a88622d1c0976e88ce15b670e7ad57e4bd6", size = 1632740, upload-time = "2026-01-03T17:32:34.793Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fe/3ea9b5af694b4e3aec0d0613a806132ca744747146fca68e96bf056f61a7/aiohttp-3.13.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4ae5b5a0e1926e504c81c5b84353e7a5516d8778fbbff00429fe7b05bb25cbce", size = 1719782, upload-time = "2026-01-03T17:32:37.737Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c2/46b3b06e60851cbb71efb0f79a3267279cbef7b12c58e68a1e897f269cca/aiohttp-3.13.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ba0eea45eb5cc3172dbfc497c066f19c41bac70963ea1a67d51fc92e4cf9a80", size = 1813527, upload-time = "2026-01-03T17:32:39.973Z" }, + { url = "https://files.pythonhosted.org/packages/36/23/71ceb78c769ed65fe4c697692de232b63dab399210678d2b00961ccb0619/aiohttp-3.13.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bae5c2ed2eae26cc382020edad80d01f36cb8e746da40b292e68fec40421dc6a", size = 1661268, upload-time = "2026-01-03T17:32:42.082Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8d/86e929523d955e85ebab7c0e2b9e0cb63604cfc27dc3280e10d0063cf682/aiohttp-3.13.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a60e60746623925eab7d25823329941aee7242d559baa119ca2b253c88a7bd6", size = 1552742, upload-time = "2026-01-03T17:32:44.622Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ea/3f5987cba1bab6bd151f0d97aa60f0ce04d3c83316692a6bb6ba2fb69f92/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e50a2e1404f063427c9d027378472316201a2290959a295169bcf25992d04558", size = 1632918, upload-time = "2026-01-03T17:32:46.749Z" }, + { url = "https://files.pythonhosted.org/packages/be/2c/7e1e85121f2e31ee938cb83a8f32dfafd4908530c10fabd6d46761c12ac7/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:9a9dc347e5a3dc7dfdbc1f82da0ef29e388ddb2ed281bfce9dd8248a313e62b7", size = 1644446, upload-time = "2026-01-03T17:32:49.063Z" }, + { url = "https://files.pythonhosted.org/packages/5d/35/ce6133d423ad0e8ca976a7c848f7146bca3520eea4ccf6b95e2d077c9d20/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b46020d11d23fe16551466c77823df9cc2f2c1e63cc965daf67fa5eec6ca1877", size = 1689487, upload-time = "2026-01-03T17:32:51.113Z" }, + { url = "https://files.pythonhosted.org/packages/50/f7/ff7a27c15603d460fd1366b3c22054f7ae4fa9310aca40b43bde35867fcd/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:69c56fbc1993fa17043e24a546959c0178fe2b5782405ad4559e6c13975c15e3", size = 1540715, upload-time = "2026-01-03T17:32:53.38Z" }, + { url = "https://files.pythonhosted.org/packages/17/02/053f11346e5b962e6d8a1c4f8c70c29d5970a1b4b8e7894c68e12c27a57f/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b99281b0704c103d4e11e72a76f1b543d4946fea7dd10767e7e1b5f00d4e5704", size = 1711835, upload-time = "2026-01-03T17:32:56.088Z" }, + { url = "https://files.pythonhosted.org/packages/fb/71/9b9761ddf276fd6708d13720197cbac19b8d67ecfa9116777924056cfcaa/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:40c5e40ecc29ba010656c18052b877a1c28f84344825efa106705e835c28530f", size = 1649593, upload-time = "2026-01-03T17:32:58.181Z" }, + { url = "https://files.pythonhosted.org/packages/ae/72/5d817e9ea218acae12a5e3b9ad1178cf0c12fc3570c0b47eea2daf95f9ea/aiohttp-3.13.3-cp39-cp39-win32.whl", hash = "sha256:56339a36b9f1fc708260c76c87e593e2afb30d26de9ae1eb445b5e051b98a7a1", size = 434831, upload-time = "2026-01-03T17:33:00.577Z" }, + { url = "https://files.pythonhosted.org/packages/39/cb/22659d9bf3149b7a2927bc2769cc9c8f8f5a80eba098398e03c199a43a85/aiohttp-3.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:c6b8568a3bb5819a0ad087f16d40e5a3fb6099f39ea1d5625a3edc1e923fc538", size = 457697, upload-time = "2026-01-03T17:33:03.167Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + +[[package]] +name = "attrs" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/8c/2c56124c6dc53a774d435f985b5973bc592f42d437be58c0c92d65ae7296/charset_normalizer-3.4.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95", size = 298751, upload-time = "2026-03-15T18:50:00.003Z" }, + { url = "https://files.pythonhosted.org/packages/86/2a/2a7db6b314b966a3bcad8c731c0719c60b931b931de7ae9f34b2839289ee/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd", size = 200027, upload-time = "2026-03-15T18:50:01.702Z" }, + { url = "https://files.pythonhosted.org/packages/68/f2/0fe775c74ae25e2a3b07b01538fc162737b3e3f795bada3bc26f4d4d495c/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4", size = 220741, upload-time = "2026-03-15T18:50:03.194Z" }, + { url = "https://files.pythonhosted.org/packages/10/98/8085596e41f00b27dd6aa1e68413d1ddda7e605f34dd546833c61fddd709/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db", size = 215802, upload-time = "2026-03-15T18:50:05.859Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ce/865e4e09b041bad659d682bbd98b47fb490b8e124f9398c9448065f64fee/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89", size = 207908, upload-time = "2026-03-15T18:50:07.676Z" }, + { url = "https://files.pythonhosted.org/packages/a8/54/8c757f1f7349262898c2f169e0d562b39dcb977503f18fdf0814e923db78/charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565", size = 194357, upload-time = "2026-03-15T18:50:09.327Z" }, + { url = "https://files.pythonhosted.org/packages/6f/29/e88f2fac9218907fc7a70722b393d1bbe8334c61fe9c46640dba349b6e66/charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9", size = 205610, upload-time = "2026-03-15T18:50:10.732Z" }, + { url = "https://files.pythonhosted.org/packages/4c/c5/21d7bb0cb415287178450171d130bed9d664211fdd59731ed2c34267b07d/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7", size = 203512, upload-time = "2026-03-15T18:50:12.535Z" }, + { url = "https://files.pythonhosted.org/packages/a4/be/ce52f3c7fdb35cc987ad38a53ebcef52eec498f4fb6c66ecfe62cfe57ba2/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550", size = 195398, upload-time = "2026-03-15T18:50:14.236Z" }, + { url = "https://files.pythonhosted.org/packages/81/a0/3ab5dd39d4859a3555e5dadfc8a9fa7f8352f8c183d1a65c90264517da0e/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0", size = 221772, upload-time = "2026-03-15T18:50:15.581Z" }, + { url = "https://files.pythonhosted.org/packages/04/6e/6a4e41a97ba6b2fa87f849c41e4d229449a586be85053c4d90135fe82d26/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8", size = 205759, upload-time = "2026-03-15T18:50:17.047Z" }, + { url = "https://files.pythonhosted.org/packages/db/3b/34a712a5ee64a6957bf355b01dc17b12de457638d436fdb05d01e463cd1c/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0", size = 216938, upload-time = "2026-03-15T18:50:18.44Z" }, + { url = "https://files.pythonhosted.org/packages/cb/05/5bd1e12da9ab18790af05c61aafd01a60f489778179b621ac2a305243c62/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b", size = 210138, upload-time = "2026-03-15T18:50:19.852Z" }, + { url = "https://files.pythonhosted.org/packages/bd/8e/3cb9e2d998ff6b21c0a1860343cb7b83eba9cdb66b91410e18fc4969d6ab/charset_normalizer-3.4.6-cp310-cp310-win32.whl", hash = "sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557", size = 144137, upload-time = "2026-03-15T18:50:21.505Z" }, + { url = "https://files.pythonhosted.org/packages/d8/8f/78f5489ffadb0db3eb7aff53d31c24531d33eb545f0c6f6567c25f49a5ff/charset_normalizer-3.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6", size = 154244, upload-time = "2026-03-15T18:50:22.81Z" }, + { url = "https://files.pythonhosted.org/packages/e4/74/e472659dffb0cadb2f411282d2d76c60da1fc94076d7fffed4ae8a93ec01/charset_normalizer-3.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058", size = 143312, upload-time = "2026-03-15T18:50:24.074Z" }, + { url = "https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e", size = 293582, upload-time = "2026-03-15T18:50:25.454Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b7/b1a117e5385cbdb3205f6055403c2a2a220c5ea80b8716c324eaf75c5c95/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9", size = 197240, upload-time = "2026-03-15T18:50:27.196Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5f/2574f0f09f3c3bc1b2f992e20bce6546cb1f17e111c5be07308dc5427956/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d", size = 217363, upload-time = "2026-03-15T18:50:28.601Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d1/0ae20ad77bc949ddd39b51bf383b6ca932f2916074c95cad34ae465ab71f/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de", size = 212994, upload-time = "2026-03-15T18:50:30.102Z" }, + { url = "https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73", size = 204697, upload-time = "2026-03-15T18:50:31.654Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/8a18fc411f085b82303cfb7154eed5bd49c77035eb7608d049468b53f87c/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c", size = 191673, upload-time = "2026-03-15T18:50:33.433Z" }, + { url = "https://files.pythonhosted.org/packages/ff/a7/11cfe61d6c5c5c7438d6ba40919d0306ed83c9ab957f3d4da2277ff67836/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc", size = 201120, upload-time = "2026-03-15T18:50:35.105Z" }, + { url = "https://files.pythonhosted.org/packages/b5/10/cf491fa1abd47c02f69687046b896c950b92b6cd7337a27e6548adbec8e4/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f", size = 200911, upload-time = "2026-03-15T18:50:36.819Z" }, + { url = "https://files.pythonhosted.org/packages/28/70/039796160b48b18ed466fde0af84c1b090c4e288fae26cd674ad04a2d703/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef", size = 192516, upload-time = "2026-03-15T18:50:38.228Z" }, + { url = "https://files.pythonhosted.org/packages/ff/34/c56f3223393d6ff3124b9e78f7de738047c2d6bc40a4f16ac0c9d7a1cb3c/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398", size = 218795, upload-time = "2026-03-15T18:50:39.664Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3b/ce2d4f86c5282191a041fdc5a4ce18f1c6bd40a5bd1f74cf8625f08d51c1/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e", size = 201833, upload-time = "2026-03-15T18:50:41.552Z" }, + { url = "https://files.pythonhosted.org/packages/3b/9b/b6a9f76b0fd7c5b5ec58b228ff7e85095370282150f0bd50b3126f5506d6/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed", size = 213920, upload-time = "2026-03-15T18:50:43.33Z" }, + { url = "https://files.pythonhosted.org/packages/ae/98/7bc23513a33d8172365ed30ee3a3b3fe1ece14a395e5fc94129541fc6003/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021", size = 206951, upload-time = "2026-03-15T18:50:44.789Z" }, + { url = "https://files.pythonhosted.org/packages/32/73/c0b86f3d1458468e11aec870e6b3feac931facbe105a894b552b0e518e79/charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e", size = 143703, upload-time = "2026-03-15T18:50:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4", size = 153857, upload-time = "2026-03-15T18:50:47.563Z" }, + { url = "https://files.pythonhosted.org/packages/e2/dc/9abe19c9b27e6cd3636036b9d1b387b78c40dedbf0b47f9366737684b4b0/charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316", size = 142751, upload-time = "2026-03-15T18:50:49.234Z" }, + { url = "https://files.pythonhosted.org/packages/e5/62/c0815c992c9545347aeea7859b50dc9044d147e2e7278329c6e02ac9a616/charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab", size = 295154, upload-time = "2026-03-15T18:50:50.88Z" }, + { url = "https://files.pythonhosted.org/packages/a8/37/bdca6613c2e3c58c7421891d80cc3efa1d32e882f7c4a7ee6039c3fc951a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21", size = 199191, upload-time = "2026-03-15T18:50:52.658Z" }, + { url = "https://files.pythonhosted.org/packages/6c/92/9934d1bbd69f7f398b38c5dae1cbf9cc672e7c34a4adf7b17c0a9c17d15d/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2", size = 218674, upload-time = "2026-03-15T18:50:54.102Z" }, + { url = "https://files.pythonhosted.org/packages/af/90/25f6ab406659286be929fd89ab0e78e38aa183fc374e03aa3c12d730af8a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff", size = 215259, upload-time = "2026-03-15T18:50:55.616Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ef/79a463eb0fff7f96afa04c1d4c51f8fc85426f918db467854bfb6a569ce3/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5", size = 207276, upload-time = "2026-03-15T18:50:57.054Z" }, + { url = "https://files.pythonhosted.org/packages/f7/72/d0426afec4b71dc159fa6b4e68f868cd5a3ecd918fec5813a15d292a7d10/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0", size = 195161, upload-time = "2026-03-15T18:50:58.686Z" }, + { url = "https://files.pythonhosted.org/packages/bf/18/c82b06a68bfcb6ce55e508225d210c7e6a4ea122bfc0748892f3dc4e8e11/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a", size = 203452, upload-time = "2026-03-15T18:51:00.196Z" }, + { url = "https://files.pythonhosted.org/packages/44/d6/0c25979b92f8adafdbb946160348d8d44aa60ce99afdc27df524379875cb/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2", size = 202272, upload-time = "2026-03-15T18:51:01.703Z" }, + { url = "https://files.pythonhosted.org/packages/2e/3d/7fea3e8fe84136bebbac715dd1221cc25c173c57a699c030ab9b8900cbb7/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5", size = 195622, upload-time = "2026-03-15T18:51:03.526Z" }, + { url = "https://files.pythonhosted.org/packages/57/8a/d6f7fd5cb96c58ef2f681424fbca01264461336d2a7fc875e4446b1f1346/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6", size = 220056, upload-time = "2026-03-15T18:51:05.269Z" }, + { url = "https://files.pythonhosted.org/packages/16/50/478cdda782c8c9c3fb5da3cc72dd7f331f031e7f1363a893cdd6ca0f8de0/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d", size = 203751, upload-time = "2026-03-15T18:51:06.858Z" }, + { url = "https://files.pythonhosted.org/packages/75/fc/cc2fcac943939c8e4d8791abfa139f685e5150cae9f94b60f12520feaa9b/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2", size = 216563, upload-time = "2026-03-15T18:51:08.564Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b7/a4add1d9a5f68f3d037261aecca83abdb0ab15960a3591d340e829b37298/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923", size = 209265, upload-time = "2026-03-15T18:51:10.312Z" }, + { url = "https://files.pythonhosted.org/packages/6c/18/c094561b5d64a24277707698e54b7f67bd17a4f857bbfbb1072bba07c8bf/charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4", size = 144229, upload-time = "2026-03-15T18:51:11.694Z" }, + { url = "https://files.pythonhosted.org/packages/ab/20/0567efb3a8fd481b8f34f739ebddc098ed062a59fed41a8d193a61939e8f/charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb", size = 154277, upload-time = "2026-03-15T18:51:13.004Z" }, + { url = "https://files.pythonhosted.org/packages/15/57/28d79b44b51933119e21f65479d0864a8d5893e494cf5daab15df0247c17/charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4", size = 142817, upload-time = "2026-03-15T18:51:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", size = 294823, upload-time = "2026-03-15T18:51:15.755Z" }, + { url = "https://files.pythonhosted.org/packages/47/7b/20e809b89c69d37be748d98e84dce6820bf663cf19cf6b942c951a3e8f41/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", size = 198527, upload-time = "2026-03-15T18:51:17.177Z" }, + { url = "https://files.pythonhosted.org/packages/37/a6/4f8d27527d59c039dce6f7622593cdcd3d70a8504d87d09eb11e9fdc6062/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf", size = 218388, upload-time = "2026-03-15T18:51:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/f6/9b/4770ccb3e491a9bacf1c46cc8b812214fe367c86a96353ccc6daf87b01ec/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8", size = 214563, upload-time = "2026-03-15T18:51:20.374Z" }, + { url = "https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", size = 206587, upload-time = "2026-03-15T18:51:21.807Z" }, + { url = "https://files.pythonhosted.org/packages/7e/70/3def227f1ec56f5c69dfc8392b8bd63b11a18ca8178d9211d7cc5e5e4f27/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88", size = 194724, upload-time = "2026-03-15T18:51:23.508Z" }, + { url = "https://files.pythonhosted.org/packages/58/ab/9318352e220c05efd31c2779a23b50969dc94b985a2efa643ed9077bfca5/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84", size = 202956, upload-time = "2026-03-15T18:51:25.239Z" }, + { url = "https://files.pythonhosted.org/packages/75/13/f3550a3ac25b70f87ac98c40d3199a8503676c2f1620efbf8d42095cfc40/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", size = 201923, upload-time = "2026-03-15T18:51:26.682Z" }, + { url = "https://files.pythonhosted.org/packages/1b/db/c5c643b912740b45e8eec21de1bbab8e7fc085944d37e1e709d3dcd9d72f/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c", size = 195366, upload-time = "2026-03-15T18:51:28.129Z" }, + { url = "https://files.pythonhosted.org/packages/5a/67/3b1c62744f9b2448443e0eb160d8b001c849ec3fef591e012eda6484787c/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194", size = 219752, upload-time = "2026-03-15T18:51:29.556Z" }, + { url = "https://files.pythonhosted.org/packages/f6/98/32ffbaf7f0366ffb0445930b87d103f6b406bc2c271563644bde8a2b1093/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc", size = 203296, upload-time = "2026-03-15T18:51:30.921Z" }, + { url = "https://files.pythonhosted.org/packages/41/12/5d308c1bbe60cabb0c5ef511574a647067e2a1f631bc8634fcafaccd8293/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f", size = 215956, upload-time = "2026-03-15T18:51:32.399Z" }, + { url = "https://files.pythonhosted.org/packages/53/e9/5f85f6c5e20669dbe56b165c67b0260547dea97dba7e187938833d791687/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", size = 208652, upload-time = "2026-03-15T18:51:34.214Z" }, + { url = "https://files.pythonhosted.org/packages/f1/11/897052ea6af56df3eef3ca94edafee410ca699ca0c7b87960ad19932c55e/charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d", size = 143940, upload-time = "2026-03-15T18:51:36.15Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389", size = 154101, upload-time = "2026-03-15T18:51:37.876Z" }, + { url = "https://files.pythonhosted.org/packages/01/a5/7abf15b4c0968e47020f9ca0935fb3274deb87cb288cd187cad92e8cdffd/charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f", size = 143109, upload-time = "2026-03-15T18:51:39.565Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, + { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, + { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, + { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, + { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, + { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, + { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, + { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, + { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, + { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, + { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, + { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, + { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, + { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, + { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, + { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, + { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, + { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, + { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, + { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, + { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, + { url = "https://files.pythonhosted.org/packages/41/85/580dbaa12ab31041ed7df59f0bebc8893514fc21da6c05c3a1c1707d118f/charset_normalizer-3.4.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31215157227939b4fb3d740cd23fe27be0439afef67b785a1eb78a3ae69cba9e", size = 298620, upload-time = "2026-03-15T18:52:57.332Z" }, + { url = "https://files.pythonhosted.org/packages/67/2c/1e55af3a5e2f52e44396d5c5b731e0ae4f3bb92915ff09a610fb2f4497eb/charset_normalizer-3.4.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecbbd45615a6885fe3240eb9db73b9e62518b611850fdf8ab08bd56de7ad2b17", size = 200106, upload-time = "2026-03-15T18:52:59.2Z" }, + { url = "https://files.pythonhosted.org/packages/10/42/0f2f51a1d16caa45fbf384fd337d4242df1a5b313babee211381d2d39a96/charset_normalizer-3.4.6-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c45a03a4c69820a399f1dda9e1d8fbf3562eda46e7720458180302021b08f778", size = 220539, upload-time = "2026-03-15T18:53:01.019Z" }, + { url = "https://files.pythonhosted.org/packages/1c/0c/4e10996c740eec0f4ae8afbbbfa25f66e8479c4b6ee9cff1ca366a4f6c04/charset_normalizer-3.4.6-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e8aeb10fcbe92767f0fa69ad5a72deca50d0dca07fbde97848997d778a50c9fe", size = 215821, upload-time = "2026-03-15T18:53:02.621Z" }, + { url = "https://files.pythonhosted.org/packages/46/73/205ae7644ebb581a7c6fa9c3751e283606e145f0e6f066003c66aafc9973/charset_normalizer-3.4.6-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54fae94be3d75f3e573c9a1b5402dc593de19377013c9a0e4285e3d402dd3a2a", size = 207917, upload-time = "2026-03-15T18:53:04.413Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ca/18f7dcf19afdab8097aeb2feb8b3809bb4b6ee356cb720abf5263d79406a/charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:2f7fdd9b6e6c529d6a2501a2d36b240109e78a8ceaef5687cfcfa2bbe671d297", size = 194513, upload-time = "2026-03-15T18:53:06.025Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6a/e7e3e204c8d79832a091e00b24595af1d5d9800d37dc1f67a6b264cc99a6/charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1d02209e06550bdaef34af58e041ad71b88e624f5d825519da3a3308e22687", size = 205612, upload-time = "2026-03-15T18:53:07.494Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ae/2169ebcea2851c5460c7a21993a0f87028be3c3e60899cb36251e1135cf5/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8bc5f0687d796c05b1e28ab0d38a50e6309906ee09375dd3aff6a9c09dd6e8f4", size = 203519, upload-time = "2026-03-15T18:53:09.048Z" }, + { url = "https://files.pythonhosted.org/packages/43/a0/6a49a925b9c225fe35dffeac5c76f68996b814c637e9d7213718f96be109/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ee4ec14bc1680d6b0afab9aea2ef27e26d2024f18b24a2d7155a52b60da7e833", size = 195411, upload-time = "2026-03-15T18:53:10.542Z" }, + { url = "https://files.pythonhosted.org/packages/47/f7/a26b0a18e52b1a0f11f53c2c400ed062f386ac227a64ae4be4c5a64699be/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d1a2ee9c1499fc8f86f4521f27a973c914b211ffa87322f4ee33bb35392da2c5", size = 221653, upload-time = "2026-03-15T18:53:12.394Z" }, + { url = "https://files.pythonhosted.org/packages/a7/3a/ed1d3b5bb55e3634bd5c31cedbe4fff79d0e5b8d9a062f663a757a07760d/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:48696db7f18afb80a068821504296eb0787d9ce239b91ca15059d1d3eaacf13b", size = 205650, upload-time = "2026-03-15T18:53:13.934Z" }, + { url = "https://files.pythonhosted.org/packages/b1/27/c75819eea5ceeefc49bae329327bb91e81adc346e2a9873d9fdb9e77cde6/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4f41da960b196ea355357285ad1316a00099f22d0929fe168343b99b254729c9", size = 216919, upload-time = "2026-03-15T18:53:15.44Z" }, + { url = "https://files.pythonhosted.org/packages/0f/42/6e91bf8b15f67b7c957091138a36057a083e60703cc27848d5e36ca1eb03/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:802168e03fba8bbc5ce0d866d589e4b1ca751d06edee69f7f3a19c5a9fe6b597", size = 210101, upload-time = "2026-03-15T18:53:17.045Z" }, + { url = "https://files.pythonhosted.org/packages/99/ff/101af2605e66a7ee59961d7f9e1060df7c92e8ea54208a02ab881422c24e/charset_normalizer-3.4.6-cp39-cp39-win32.whl", hash = "sha256:8761ac29b6c81574724322a554605608a9960769ea83d2c73e396f3df896ad54", size = 144136, upload-time = "2026-03-15T18:53:19.152Z" }, + { url = "https://files.pythonhosted.org/packages/1d/da/de5942dfbf21f28c19e9202267dabf7bc73f195465d020a3a60054520cc5/charset_normalizer-3.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:1cf0a70018692f85172348fe06d3a4b63f94ecb055e13a00c644d368eb82e5b8", size = 154210, upload-time = "2026-03-15T18:53:20.576Z" }, + { url = "https://files.pythonhosted.org/packages/06/df/1b780a25b86d22b1d736f6ac883afd38ffdf30ddc18e5dc0e82211f493f1/charset_normalizer-3.4.6-cp39-cp39-win_arm64.whl", hash = "sha256:3516bbb8d42169de9e61b8520cbeeeb716f12f4ecfe3fd30a9919aa16c806ca8", size = 143225, upload-time = "2026-03-15T18:53:22.072Z" }, + { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/c2/59/ae5cdac87a00962122ea37bb346d41b66aec05f9ce328fa2b9e216f8967b/frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47", size = 86967, upload-time = "2025-10-06T05:37:55.607Z" }, + { url = "https://files.pythonhosted.org/packages/8a/10/17059b2db5a032fd9323c41c39e9d1f5f9d0c8f04d1e4e3e788573086e61/frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca", size = 49984, upload-time = "2025-10-06T05:37:57.049Z" }, + { url = "https://files.pythonhosted.org/packages/4b/de/ad9d82ca8e5fa8f0c636e64606553c79e2b859ad253030b62a21fe9986f5/frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068", size = 50240, upload-time = "2025-10-06T05:37:58.145Z" }, + { url = "https://files.pythonhosted.org/packages/4e/45/3dfb7767c2a67d123650122b62ce13c731b6c745bc14424eea67678b508c/frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95", size = 219472, upload-time = "2025-10-06T05:37:59.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/bf/5bf23d913a741b960d5c1dac7c1985d8a2a1d015772b2d18ea168b08e7ff/frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459", size = 221531, upload-time = "2025-10-06T05:38:00.521Z" }, + { url = "https://files.pythonhosted.org/packages/d0/03/27ec393f3b55860859f4b74cdc8c2a4af3dbf3533305e8eacf48a4fd9a54/frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675", size = 219211, upload-time = "2025-10-06T05:38:01.842Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ad/0fd00c404fa73fe9b169429e9a972d5ed807973c40ab6b3cf9365a33d360/frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61", size = 231775, upload-time = "2025-10-06T05:38:03.384Z" }, + { url = "https://files.pythonhosted.org/packages/8a/c3/86962566154cb4d2995358bc8331bfc4ea19d07db1a96f64935a1607f2b6/frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6", size = 236631, upload-time = "2025-10-06T05:38:04.609Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/6ffad161dbd83782d2c66dc4d378a9103b31770cb1e67febf43aea42d202/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5", size = 218632, upload-time = "2025-10-06T05:38:05.917Z" }, + { url = "https://files.pythonhosted.org/packages/58/b2/4677eee46e0a97f9b30735e6ad0bf6aba3e497986066eb68807ac85cf60f/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3", size = 235967, upload-time = "2025-10-06T05:38:07.614Z" }, + { url = "https://files.pythonhosted.org/packages/05/f3/86e75f8639c5a93745ca7addbbc9de6af56aebb930d233512b17e46f6493/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1", size = 228799, upload-time = "2025-10-06T05:38:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/30/00/39aad3a7f0d98f5eb1d99a3c311215674ed87061aecee7851974b335c050/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178", size = 230566, upload-time = "2025-10-06T05:38:10.52Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4d/aa144cac44568d137846ddc4d5210fb5d9719eb1d7ec6fa2728a54b5b94a/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda", size = 217715, upload-time = "2025-10-06T05:38:11.832Z" }, + { url = "https://files.pythonhosted.org/packages/64/4c/8f665921667509d25a0dd72540513bc86b356c95541686f6442a3283019f/frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087", size = 39933, upload-time = "2025-10-06T05:38:13.061Z" }, + { url = "https://files.pythonhosted.org/packages/79/bd/bcc926f87027fad5e59926ff12d136e1082a115025d33c032d1cd69ab377/frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a", size = 44121, upload-time = "2025-10-06T05:38:14.572Z" }, + { url = "https://files.pythonhosted.org/packages/4c/07/9c2e4eb7584af4b705237b971b89a4155a8e57599c4483a131a39256a9a0/frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103", size = 40312, upload-time = "2025-10-06T05:38:15.699Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -45,6 +499,300 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/0b/19348d4c98980c4851d2f943f8ebafdece2ae7ef737adcfa5994ce8e5f10/multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", size = 77176, upload-time = "2026-01-26T02:42:59.784Z" }, + { url = "https://files.pythonhosted.org/packages/ef/04/9de3f8077852e3d438215c81e9b691244532d2e05b4270e89ce67b7d103c/multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", size = 44996, upload-time = "2026-01-26T02:43:01.674Z" }, + { url = "https://files.pythonhosted.org/packages/31/5c/08c7f7fe311f32e83f7621cd3f99d805f45519cd06fafb247628b861da7d/multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", size = 44631, upload-time = "2026-01-26T02:43:03.169Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7f/0e3b1390ae772f27501199996b94b52ceeb64fe6f9120a32c6c3f6b781be/multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", size = 242561, upload-time = "2026-01-26T02:43:04.733Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f4/8719f4f167586af317b69dd3e90f913416c91ca610cac79a45c53f590312/multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", size = 242223, upload-time = "2026-01-26T02:43:06.695Z" }, + { url = "https://files.pythonhosted.org/packages/47/ab/7c36164cce64a6ad19c6d9a85377b7178ecf3b89f8fd589c73381a5eedfd/multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", size = 222322, upload-time = "2026-01-26T02:43:08.472Z" }, + { url = "https://files.pythonhosted.org/packages/f5/79/a25add6fb38035b5337bc5734f296d9afc99163403bbcf56d4170f97eb62/multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", size = 254005, upload-time = "2026-01-26T02:43:10.127Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7b/64a87cf98e12f756fc8bd444b001232ffff2be37288f018ad0d3f0aae931/multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", size = 251173, upload-time = "2026-01-26T02:43:11.731Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ac/b605473de2bb404e742f2cc3583d12aedb2352a70e49ae8fce455b50c5aa/multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", size = 243273, upload-time = "2026-01-26T02:43:13.063Z" }, + { url = "https://files.pythonhosted.org/packages/03/65/11492d6a0e259783720f3bc1d9ea55579a76f1407e31ed44045c99542004/multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", size = 238956, upload-time = "2026-01-26T02:43:14.843Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a7/7ee591302af64e7c196fb63fe856c788993c1372df765102bd0448e7e165/multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", size = 233477, upload-time = "2026-01-26T02:43:16.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/99/c109962d58756c35fd9992fed7f2355303846ea2ff054bb5f5e9d6b888de/multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", size = 243615, upload-time = "2026-01-26T02:43:17.84Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5f/1973e7c771c86e93dcfe1c9cc55a5481b610f6614acfc28c0d326fe6bfad/multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", size = 249930, upload-time = "2026-01-26T02:43:19.06Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a5/f170fc2268c3243853580203378cd522446b2df632061e0a5409817854c7/multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", size = 243807, upload-time = "2026-01-26T02:43:20.286Z" }, + { url = "https://files.pythonhosted.org/packages/de/01/73856fab6d125e5bc652c3986b90e8699a95e84b48d72f39ade6c0e74a8c/multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", size = 239103, upload-time = "2026-01-26T02:43:21.508Z" }, + { url = "https://files.pythonhosted.org/packages/e7/46/f1220bd9944d8aa40d8ccff100eeeee19b505b857b6f603d6078cb5315b0/multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", size = 41416, upload-time = "2026-01-26T02:43:22.703Z" }, + { url = "https://files.pythonhosted.org/packages/68/00/9b38e272a770303692fc406c36e1a4c740f401522d5787691eb38a8925a8/multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", size = 46022, upload-time = "2026-01-26T02:43:23.77Z" }, + { url = "https://files.pythonhosted.org/packages/64/65/d8d42490c02ee07b6bbe00f7190d70bb4738b3cce7629aaf9f213ef730dd/multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", size = 43238, upload-time = "2026-01-26T02:43:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z" }, + { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ee/74525ebe3eb5fddcd6735fc03cbea3feeed4122b53bc798ac32d297ac9ae/multidict-6.7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:65573858d27cdeaca41893185677dc82395159aa28875a8867af66532d413a8f", size = 77107, upload-time = "2026-01-26T02:46:12.608Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9a/ce8744e777a74b3050b1bf56be3eed1053b3457302ea055f1ea437200a23/multidict-6.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c524c6fb8fc342793708ab111c4dbc90ff9abd568de220432500e47e990c0358", size = 44943, upload-time = "2026-01-26T02:46:14.016Z" }, + { url = "https://files.pythonhosted.org/packages/83/9c/1d2a283d9c6f31e260cb6c2fccadc3edcf6c4c14ee0929cd2af4d2606dd7/multidict-6.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aa23b001d968faef416ff70dc0f1ab045517b9b42a90edd3e9bcdb06479e31d5", size = 44603, upload-time = "2026-01-26T02:46:15.391Z" }, + { url = "https://files.pythonhosted.org/packages/87/9d/3b186201671583d8e8d6d79c07481a5aafd0ba7575e3d8566baec80c1e82/multidict-6.7.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6704fa2b7453b2fb121740555fa1ee20cd98c4d011120caf4d2b8d4e7c76eec0", size = 240573, upload-time = "2026-01-26T02:46:16.783Z" }, + { url = "https://files.pythonhosted.org/packages/42/7d/a52f5d4d0754311d1ac78478e34dff88de71259a8585e05ee14e5f877caf/multidict-6.7.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:121a34e5bfa410cdf2c8c49716de160de3b1dbcd86b49656f5681e4543bcd1a8", size = 240106, upload-time = "2026-01-26T02:46:18.432Z" }, + { url = "https://files.pythonhosted.org/packages/84/9f/d80118e6c30ff55b7d171bdc5520aad4b9626e657520b8d7c8ca8c2fad12/multidict-6.7.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:026d264228bcd637d4e060844e39cdc60f86c479e463d49075dedc21b18fbbe0", size = 219418, upload-time = "2026-01-26T02:46:20.526Z" }, + { url = "https://files.pythonhosted.org/packages/c7/bd/896e60b3457f194de77c7de64f9acce9f75da0518a5230ce1df534f6747b/multidict-6.7.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e697826df7eb63418ee190fd06ce9f1803593bb4b9517d08c60d9b9a7f69d8f", size = 252124, upload-time = "2026-01-26T02:46:22.157Z" }, + { url = "https://files.pythonhosted.org/packages/f4/de/ba6b30447c36a37078d0ba604aa12c1a52887af0c355236ca6e0a9d5286f/multidict-6.7.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb08271280173720e9fea9ede98e5231defcbad90f1624bea26f32ec8a956e2f", size = 249402, upload-time = "2026-01-26T02:46:23.718Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b2/50a383c96230e432895a2fd3bcfe1b65785899598259d871d5de6b93180c/multidict-6.7.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6b3228e1d80af737b72925ce5fb4daf5a335e49cd7ab77ed7b9fdfbf58c526e", size = 240346, upload-time = "2026-01-26T02:46:25.393Z" }, + { url = "https://files.pythonhosted.org/packages/89/37/16d391fd8da544b1489306e38a46785fa41dd0f0ef766837ed7d4676dde0/multidict-6.7.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3943debf0fbb57bdde5901695c11094a9a36723e5c03875f87718ee15ca2f4d2", size = 237010, upload-time = "2026-01-26T02:46:27.408Z" }, + { url = "https://files.pythonhosted.org/packages/b0/24/3152ee026eda86d5d3e3685182911e6951af7a016579da931080ce6ac9ad/multidict-6.7.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:98c5787b0a0d9a41d9311eae44c3b76e6753def8d8870ab501320efe75a6a5f8", size = 232018, upload-time = "2026-01-26T02:46:29.941Z" }, + { url = "https://files.pythonhosted.org/packages/9c/1f/48d3c27a72be7fd23a55d8847193c459959bf35a5bb5844530dab00b739b/multidict-6.7.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:08ccb2a6dc72009093ebe7f3f073e5ec5964cba9a706fa94b1a1484039b87941", size = 241498, upload-time = "2026-01-26T02:46:32.052Z" }, + { url = "https://files.pythonhosted.org/packages/1a/45/413643ae2952d0decdf6c1250f86d08a43e143271441e81027e38d598bd7/multidict-6.7.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb351f72c26dc9abe338ca7294661aa22969ad8ffe7ef7d5541d19f368dc854a", size = 247957, upload-time = "2026-01-26T02:46:33.666Z" }, + { url = "https://files.pythonhosted.org/packages/50/f8/f1d0ac23df15e0470776388bdb261506f63af1f81d28bacb5e262d6e12b6/multidict-6.7.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ac1c665bad8b5d762f5f85ebe4d94130c26965f11de70c708c75671297c776de", size = 241651, upload-time = "2026-01-26T02:46:35.7Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c9/1a2a18f383cf129add66b6c36b75c3911a7ba95cf26cb141482de085cc12/multidict-6.7.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fa6609d0364f4f6f58351b4659a1f3e0e898ba2a8c5cac04cb2c7bc556b0bc5", size = 236371, upload-time = "2026-01-26T02:46:37.37Z" }, + { url = "https://files.pythonhosted.org/packages/bb/aa/77d87e3fca31325b87e0eb72d5fe9a7472dcb51391a42df7ac1f3842f6c0/multidict-6.7.1-cp39-cp39-win32.whl", hash = "sha256:6f77ce314a29263e67adadc7e7c1bc699fcb3a305059ab973d038f87caa42ed0", size = 41426, upload-time = "2026-01-26T02:46:39.026Z" }, + { url = "https://files.pythonhosted.org/packages/e3/b3/e8863e6a2da15a9d7e98976ff402e871b7352c76566df6c18d0378e0d9cf/multidict-6.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:f537b55778cd3cbee430abe3131255d3a78202e0f9ea7ffc6ada893a4bcaeea4", size = 46180, upload-time = "2026-01-26T02:46:40.422Z" }, + { url = "https://files.pythonhosted.org/packages/93/d3/dd4fa951ad5b5fa216bf30054d705683d13405eea7459833d78f31b74c9c/multidict-6.7.1-cp39-cp39-win_arm64.whl", hash = "sha256:749aa54f578f2e5f439538706a475aa844bfa8ef75854b1401e6e528e4937cf9", size = 43231, upload-time = "2026-01-26T02:46:41.945Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, + { url = "https://files.pythonhosted.org/packages/9b/01/0ebaec9003f5d619a7475165961f8e3083cf8644d704b60395df3601632d/propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff", size = 80277, upload-time = "2025-10-08T19:48:36.647Z" }, + { url = "https://files.pythonhosted.org/packages/34/58/04af97ac586b4ef6b9026c3fd36ee7798b737a832f5d3440a4280dcebd3a/propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb", size = 45865, upload-time = "2025-10-08T19:48:37.859Z" }, + { url = "https://files.pythonhosted.org/packages/7c/19/b65d98ae21384518b291d9939e24a8aeac4fdb5101b732576f8f7540e834/propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac", size = 47636, upload-time = "2025-10-08T19:48:39.038Z" }, + { url = "https://files.pythonhosted.org/packages/b3/0f/317048c6d91c356c7154dca5af019e6effeb7ee15fa6a6db327cc19e12b4/propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888", size = 201126, upload-time = "2025-10-08T19:48:40.774Z" }, + { url = "https://files.pythonhosted.org/packages/71/69/0b2a7a5a6ee83292b4b997dbd80549d8ce7d40b6397c1646c0d9495f5a85/propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc", size = 209837, upload-time = "2025-10-08T19:48:42.167Z" }, + { url = "https://files.pythonhosted.org/packages/a5/92/c699ac495a6698df6e497fc2de27af4b6ace10d8e76528357ce153722e45/propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a", size = 215578, upload-time = "2025-10-08T19:48:43.56Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ee/14de81c5eb02c0ee4f500b4e39c4e1bd0677c06e72379e6ab18923c773fc/propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88", size = 197187, upload-time = "2025-10-08T19:48:45.309Z" }, + { url = "https://files.pythonhosted.org/packages/1d/94/48dce9aaa6d8dd5a0859bad75158ec522546d4ac23f8e2f05fac469477dd/propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00", size = 193478, upload-time = "2025-10-08T19:48:47.743Z" }, + { url = "https://files.pythonhosted.org/packages/60/b5/0516b563e801e1ace212afde869a0596a0d7115eec0b12d296d75633fb29/propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0", size = 190650, upload-time = "2025-10-08T19:48:49.373Z" }, + { url = "https://files.pythonhosted.org/packages/24/89/e0f7d4a5978cd56f8cd67735f74052f257dc471ec901694e430f0d1572fe/propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e", size = 200251, upload-time = "2025-10-08T19:48:51.4Z" }, + { url = "https://files.pythonhosted.org/packages/06/7d/a1fac863d473876ed4406c914f2e14aa82d2f10dd207c9e16fc383cc5a24/propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781", size = 200919, upload-time = "2025-10-08T19:48:53.227Z" }, + { url = "https://files.pythonhosted.org/packages/c3/4e/f86a256ff24944cf5743e4e6c6994e3526f6acfcfb55e21694c2424f758c/propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183", size = 193211, upload-time = "2025-10-08T19:48:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/6e/3f/3fbad5f4356b068f1b047d300a6ff2c66614d7030f078cd50be3fec04228/propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19", size = 38314, upload-time = "2025-10-08T19:48:56.792Z" }, + { url = "https://files.pythonhosted.org/packages/a4/45/d78d136c3a3d215677abb886785aae744da2c3005bcb99e58640c56529b1/propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f", size = 41912, upload-time = "2025-10-08T19:48:57.995Z" }, + { url = "https://files.pythonhosted.org/packages/fc/2a/b0632941f25139f4e58450b307242951f7c2717a5704977c6d5323a800af/propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938", size = 38450, upload-time = "2025-10-08T19:48:59.349Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +] + [[package]] name = "pygments" version = "2.19.2" @@ -55,12 +803,17 @@ wheels = [ ] [[package]] -name = "pysdl2" -version = "0.9.17" +name = "pysdl3" +version = "0.9.11b0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/ff/8704d84ad4d25f0a7bf7912504f64575e432e8d57dfba2fe35f5b2db7e04/pysdl2-0.9.17.tar.gz", hash = "sha256:48c6ef01a4eb123db5f7e46e1a1b565675755b07e615f3fe20a623c94735b52b", size = 775955, upload-time = "2024-12-30T18:07:27.562Z" } +dependencies = [ + { name = "aiohttp" }, + { name = "packaging" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/2a/86e2b1cc7ac5a186ac3bad262cfbe55313268b6a8f7a3e61a4212fa4af09/pysdl3-0.9.11b0.tar.gz", hash = "sha256:e87dce397221c4943763bb45ec8725deb07b2bf5636d5da875e9d0ae34504068", size = 1421769, upload-time = "2026-03-15T19:28:04.222Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/e2/399ea7900e7510096aeb41439e6f1540bef0bdf7e15cc2d8464e4adb71e8/PySDL2-0.9.17-py3-none-any.whl", hash = "sha256:fe923dbf5c7b27bbc1eb2bf58abfa793f8f13fd7ae8b27b1bc2de49920bcbd41", size = 583137, upload-time = "2024-12-30T18:07:25.987Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ff/58a5b5a5bbf1fb822359c8fcf1a307d9dadc078d988cd6e770f4381081bf/pysdl3-0.9.11b0-py3-none-any.whl", hash = "sha256:98bd71dd4e1bcdaa5e83da200a208439048690da627b2d19163f5e222518cb24", size = 99667, upload-time = "2026-03-15T19:28:02.81Z" }, ] [[package]] @@ -72,6 +825,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" }, ] +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + [[package]] name = "rich" version = "14.3.3" @@ -91,14 +859,320 @@ name = "switch-pico-bridge" version = "0.1.0" source = { editable = "." } dependencies = [ - { name = "pysdl2" }, + { name = "pysdl3" }, { name = "pyserial" }, { name = "rich" }, ] [package.metadata] requires-dist = [ - { name = "pysdl2" }, + { name = "pysdl3" }, { name = "pyserial" }, { name = "rich" }, ] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "yarl" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "idna", marker = "python_full_version < '3.10'" }, + { name = "multidict", marker = "python_full_version < '3.10'" }, + { name = "propcache", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517, upload-time = "2025-10-06T14:08:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495, upload-time = "2025-10-06T14:08:46.2Z" }, + { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400, upload-time = "2025-10-06T14:08:47.855Z" }, + { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545, upload-time = "2025-10-06T14:08:49.683Z" }, + { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598, upload-time = "2025-10-06T14:08:51.215Z" }, + { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893, upload-time = "2025-10-06T14:08:53.144Z" }, + { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240, upload-time = "2025-10-06T14:08:55.036Z" }, + { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965, upload-time = "2025-10-06T14:08:56.722Z" }, + { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026, upload-time = "2025-10-06T14:08:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637, upload-time = "2025-10-06T14:09:00.506Z" }, + { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082, upload-time = "2025-10-06T14:09:01.936Z" }, + { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811, upload-time = "2025-10-06T14:09:03.445Z" }, + { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223, upload-time = "2025-10-06T14:09:05.401Z" }, + { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118, upload-time = "2025-10-06T14:09:11.148Z" }, + { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852, upload-time = "2025-10-06T14:09:12.958Z" }, + { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" }, + { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" }, + { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" }, + { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" }, + { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" }, + { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" }, + { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" }, + { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" }, + { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" }, + { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" }, + { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" }, + { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" }, + { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, + { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, + { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, + { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, + { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, + { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, + { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, + { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, + { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, + { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, + { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, + { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, + { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, + { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, + { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, + { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, + { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, + { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, + { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, + { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, + { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, + { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, + { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, + { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, + { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, + { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, + { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, + { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, + { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, + { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, + { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, + { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, + { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, + { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, + { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, + { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, + { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, + { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, + { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, + { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, + { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, + { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, + { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, + { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, + { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, + { url = "https://files.pythonhosted.org/packages/94/fd/6480106702a79bcceda5fd9c63cb19a04a6506bd5ce7fd8d9b63742f0021/yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748", size = 141301, upload-time = "2025-10-06T14:12:19.01Z" }, + { url = "https://files.pythonhosted.org/packages/42/e1/6d95d21b17a93e793e4ec420a925fe1f6a9342338ca7a563ed21129c0990/yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859", size = 93864, upload-time = "2025-10-06T14:12:21.05Z" }, + { url = "https://files.pythonhosted.org/packages/32/58/b8055273c203968e89808413ea4c984988b6649baabf10f4522e67c22d2f/yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9", size = 94706, upload-time = "2025-10-06T14:12:23.287Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/d7bfbc28a88c2895ecd0da6a874def0c147de78afc52c773c28e1aa233a3/yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054", size = 347100, upload-time = "2025-10-06T14:12:28.527Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e8/37a1e7b99721c0564b1fc7b0a4d1f595ef6fb8060d82ca61775b644185f7/yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b", size = 318902, upload-time = "2025-10-06T14:12:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ef/34724449d7ef2db4f22df644f2dac0b8a275d20f585e526937b3ae47b02d/yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60", size = 363302, upload-time = "2025-10-06T14:12:32.295Z" }, + { url = "https://files.pythonhosted.org/packages/8a/04/88a39a5dad39889f192cce8d66cc4c58dbeca983e83f9b6bf23822a7ed91/yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890", size = 370816, upload-time = "2025-10-06T14:12:34.01Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1f/5e895e547129413f56c76be2c3ce4b96c797d2d0ff3e16a817d9269b12e6/yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba", size = 346465, upload-time = "2025-10-06T14:12:35.977Z" }, + { url = "https://files.pythonhosted.org/packages/11/13/a750e9fd6f9cc9ed3a52a70fe58ffe505322f0efe0d48e1fd9ffe53281f5/yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca", size = 341506, upload-time = "2025-10-06T14:12:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/3c/67/bb6024de76e7186611ebe626aec5b71a2d2ecf9453e795f2dbd80614784c/yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba", size = 335030, upload-time = "2025-10-06T14:12:39.775Z" }, + { url = "https://files.pythonhosted.org/packages/a2/be/50b38447fd94a7992996a62b8b463d0579323fcfc08c61bdba949eef8a5d/yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b", size = 358560, upload-time = "2025-10-06T14:12:41.547Z" }, + { url = "https://files.pythonhosted.org/packages/e2/89/c020b6f547578c4e3dbb6335bf918f26e2f34ad0d1e515d72fd33ac0c635/yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e", size = 357290, upload-time = "2025-10-06T14:12:43.861Z" }, + { url = "https://files.pythonhosted.org/packages/8c/52/c49a619ee35a402fa3a7019a4fa8d26878fec0d1243f6968bbf516789578/yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8", size = 350700, upload-time = "2025-10-06T14:12:46.868Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c9/f5042d87777bf6968435f04a2bbb15466b2f142e6e47fa4f34d1a3f32f0c/yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b", size = 82323, upload-time = "2025-10-06T14:12:48.633Z" }, + { url = "https://files.pythonhosted.org/packages/fd/58/d00f7cad9eba20c4eefac2682f34661d1d1b3a942fc0092eb60e78cfb733/yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed", size = 87145, upload-time = "2025-10-06T14:12:50.241Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a3/70904f365080780d38b919edd42d224b8c4ce224a86950d2eaa2a24366ad/yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2", size = 82173, upload-time = "2025-10-06T14:12:51.869Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, +] + +[[package]] +name = "yarl" +version = "1.23.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +dependencies = [ + { name = "idna", marker = "python_full_version >= '3.10'" }, + { name = "multidict", marker = "python_full_version >= '3.10'" }, + { name = "propcache", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/0d/9cc638702f6fc3c7a3685bcc8cf2a9ed7d6206e932a49f5242658047ef51/yarl-1.23.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cff6d44cb13d39db2663a22b22305d10855efa0fa8015ddeacc40bc59b9d8107", size = 123764, upload-time = "2026-03-01T22:04:09.7Z" }, + { url = "https://files.pythonhosted.org/packages/7a/35/5a553687c5793df5429cd1db45909d4f3af7eee90014888c208d086a44f0/yarl-1.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c53f8347cd4200f0d70a48ad059cabaf24f5adc6ba08622a23423bc7efa10d", size = 86282, upload-time = "2026-03-01T22:04:11.892Z" }, + { url = "https://files.pythonhosted.org/packages/68/2e/c5a2234238f8ce37a8312b52801ee74117f576b1539eec8404a480434acc/yarl-1.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a6940a074fb3c48356ed0158a3ca5699c955ee4185b4d7d619be3c327143e05", size = 86053, upload-time = "2026-03-01T22:04:13.292Z" }, + { url = "https://files.pythonhosted.org/packages/74/3f/bbd8ff36fb038622797ffbaf7db314918bb4d76f1cc8a4f9ca7a55fe5195/yarl-1.23.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed5f69ce7be7902e5c70ea19eb72d20abf7d725ab5d49777d696e32d4fc1811d", size = 99395, upload-time = "2026-03-01T22:04:15.133Z" }, + { url = "https://files.pythonhosted.org/packages/77/04/9516bc4e269d2a3ec9c6779fcdeac51ce5b3a9b0156f06ac7152e5bba864/yarl-1.23.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:389871e65468400d6283c0308e791a640b5ab5c83bcee02a2f51295f95e09748", size = 92143, upload-time = "2026-03-01T22:04:16.829Z" }, + { url = "https://files.pythonhosted.org/packages/c7/63/88802d1f6b1cb1fc67d67a58cd0cf8a1790de4ce7946e434240f1d60ab4a/yarl-1.23.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dda608c88cf709b1d406bdfcd84d8d63cff7c9e577a403c6108ce8ce9dcc8764", size = 107643, upload-time = "2026-03-01T22:04:18.519Z" }, + { url = "https://files.pythonhosted.org/packages/8e/db/4f9b838f4d8bdd6f0f385aed8bbf21c71ed11a0b9983305c302cbd557815/yarl-1.23.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c4fe09e0780c6c3bf2b7d4af02ee2394439d11a523bbcf095cf4747c2932007", size = 108700, upload-time = "2026-03-01T22:04:20.373Z" }, + { url = "https://files.pythonhosted.org/packages/50/12/95a1d33f04a79c402664070d43b8b9f72dc18914e135b345b611b0b1f8cc/yarl-1.23.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c9921eb8bd12633b41ad27686bbb0b1a2a9b8452bfdf221e34f311e9942ed4", size = 102769, upload-time = "2026-03-01T22:04:23.055Z" }, + { url = "https://files.pythonhosted.org/packages/86/65/91a0285f51321369fd1a8308aa19207520c5f0587772cfc2e03fc2467e90/yarl-1.23.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5f10fd85e4b75967468af655228fbfd212bdf66db1c0d135065ce288982eda26", size = 101114, upload-time = "2026-03-01T22:04:25.031Z" }, + { url = "https://files.pythonhosted.org/packages/58/80/c7c8244fc3e5bc483dc71a09560f43b619fab29301a0f0a8f936e42865c7/yarl-1.23.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dbf507e9ef5688bada447a24d68b4b58dd389ba93b7afc065a2ba892bea54769", size = 98883, upload-time = "2026-03-01T22:04:27.281Z" }, + { url = "https://files.pythonhosted.org/packages/86/e7/71ca9cc9ca79c0b7d491216177d1aed559d632947b8ffb0ee60f7d8b23e3/yarl-1.23.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:85e9beda1f591bc73e77ea1c51965c68e98dafd0fec72cdd745f77d727466716", size = 94172, upload-time = "2026-03-01T22:04:28.554Z" }, + { url = "https://files.pythonhosted.org/packages/6a/3f/6c6c8a0fe29c26fb2db2e8d32195bb84ec1bfb8f1d32e7f73b787fcf349b/yarl-1.23.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0e1fdaa14ef51366d7757b45bde294e95f6c8c049194e793eedb8387c86d5993", size = 107010, upload-time = "2026-03-01T22:04:30.385Z" }, + { url = "https://files.pythonhosted.org/packages/56/38/12730c05e5ad40a76374d440ed8b0899729a96c250516d91c620a6e38fc2/yarl-1.23.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:75e3026ab649bf48f9a10c0134512638725b521340293f202a69b567518d94e0", size = 100285, upload-time = "2026-03-01T22:04:31.752Z" }, + { url = "https://files.pythonhosted.org/packages/34/92/6a7be9239f2347234e027284e7a5f74b1140cc86575e7b469d13fba1ebfe/yarl-1.23.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:80e6d33a3d42a7549b409f199857b4fb54e2103fc44fb87605b6663b7a7ff750", size = 108230, upload-time = "2026-03-01T22:04:33.844Z" }, + { url = "https://files.pythonhosted.org/packages/5e/81/4aebccfa9376bd98b9d8bfad20621a57d3e8cfc5b8631c1fa5f62cdd03f4/yarl-1.23.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5ec2f42d41ccbd5df0270d7df31618a8ee267bfa50997f5d720ddba86c4a83a6", size = 103008, upload-time = "2026-03-01T22:04:35.856Z" }, + { url = "https://files.pythonhosted.org/packages/38/0f/0b4e3edcec794a86b853b0c6396c0a888d72dfce19b2d88c02ac289fb6c1/yarl-1.23.0-cp310-cp310-win32.whl", hash = "sha256:debe9c4f41c32990771be5c22b56f810659f9ddf3d63f67abfdcaa2c6c9c5c1d", size = 83073, upload-time = "2026-03-01T22:04:38.268Z" }, + { url = "https://files.pythonhosted.org/packages/a0/71/ad95c33da18897e4c636528bbc24a1dd23fe16797de8bc4ec667b8db0ba4/yarl-1.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f043cb8a2d71c981c09c510da013bc79fd661f5c60139f00dd3c3cc4f2ffb", size = 87328, upload-time = "2026-03-01T22:04:39.558Z" }, + { url = "https://files.pythonhosted.org/packages/e2/14/dfa369523c79bccf9c9c746b0a63eb31f65db9418ac01275f7950962e504/yarl-1.23.0-cp310-cp310-win_arm64.whl", hash = "sha256:263cd4f47159c09b8b685890af949195b51d1aa82ba451c5847ca9bc6413c220", size = 82463, upload-time = "2026-03-01T22:04:41.454Z" }, + { url = "https://files.pythonhosted.org/packages/a2/aa/60da938b8f0997ba3a911263c40d82b6f645a67902a490b46f3355e10fae/yarl-1.23.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99", size = 123641, upload-time = "2026-03-01T22:04:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c", size = 86248, upload-time = "2026-03-01T22:04:44.757Z" }, + { url = "https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432", size = 85988, upload-time = "2026-03-01T22:04:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6c/4a90d59c572e46b270ca132aca66954f1175abd691f74c1ef4c6711828e2/yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a", size = 100566, upload-time = "2026-03-01T22:04:47.639Z" }, + { url = "https://files.pythonhosted.org/packages/49/fb/c438fb5108047e629f6282a371e6e91cf3f97ee087c4fb748a1f32ceef55/yarl-1.23.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05", size = 92079, upload-time = "2026-03-01T22:04:48.925Z" }, + { url = "https://files.pythonhosted.org/packages/d9/13/d269aa1aed3e4f50a5a103f96327210cc5fa5dd2d50882778f13c7a14606/yarl-1.23.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83", size = 108741, upload-time = "2026-03-01T22:04:50.838Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/115b16f22c37ea4437d323e472945bea97301c8ec6089868fa560abab590/yarl-1.23.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c", size = 108099, upload-time = "2026-03-01T22:04:52.499Z" }, + { url = "https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598", size = 102678, upload-time = "2026-03-01T22:04:55.176Z" }, + { url = "https://files.pythonhosted.org/packages/85/59/cd98e556fbb2bf8fab29c1a722f67ad45c5f3447cac798ab85620d1e70af/yarl-1.23.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b", size = 100803, upload-time = "2026-03-01T22:04:56.588Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c0/b39770b56d4a9f0bb5f77e2f1763cd2d75cc2f6c0131e3b4c360348fcd65/yarl-1.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c", size = 100163, upload-time = "2026-03-01T22:04:58.492Z" }, + { url = "https://files.pythonhosted.org/packages/e7/64/6980f99ab00e1f0ff67cb84766c93d595b067eed07439cfccfc8fb28c1a6/yarl-1.23.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788", size = 93859, upload-time = "2026-03-01T22:05:00.268Z" }, + { url = "https://files.pythonhosted.org/packages/38/69/912e6c5e146793e5d4b5fe39ff5b00f4d22463dfd5a162bec565ac757673/yarl-1.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222", size = 108202, upload-time = "2026-03-01T22:05:02.273Z" }, + { url = "https://files.pythonhosted.org/packages/59/97/35ca6767524687ad64e5f5c31ad54bc76d585585a9fcb40f649e7e82ffed/yarl-1.23.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb", size = 99866, upload-time = "2026-03-01T22:05:03.597Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1c/1a3387ee6d73589f6f2a220ae06f2984f6c20b40c734989b0a44f5987308/yarl-1.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc", size = 107852, upload-time = "2026-03-01T22:05:04.986Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b8/35c0750fcd5a3f781058bfd954515dd4b1eab45e218cbb85cf11132215f1/yarl-1.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2", size = 102919, upload-time = "2026-03-01T22:05:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1c/9a1979aec4a81896d597bcb2177827f2dbee3f5b7cc48b2d0dadb644b41d/yarl-1.23.0-cp311-cp311-win32.whl", hash = "sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5", size = 82602, upload-time = "2026-03-01T22:05:08.444Z" }, + { url = "https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46", size = 87461, upload-time = "2026-03-01T22:05:10.145Z" }, + { url = "https://files.pythonhosted.org/packages/93/95/07e3553fe6f113e6864a20bdc53a78113cda3b9ced8784ee52a52c9f80d8/yarl-1.23.0-cp311-cp311-win_arm64.whl", hash = "sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928", size = 82336, upload-time = "2026-03-01T22:05:11.554Z" }, + { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, + { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, + { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, + { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, + { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, + { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, + { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, + { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, + { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, + { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, + { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, + { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, + { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, + { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, + { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, + { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, + { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, + { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, + { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, + { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, + { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, + { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, + { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, + { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, + { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, + { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, + { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, + { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, + { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, + { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, + { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, + { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, + { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, + { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, + { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, + { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, + { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, + { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, + { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, + { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, + { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, + { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, + { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, + { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, + { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, + { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, + { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, + { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, + { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, + { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, + { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, + { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, + { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, + { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, + { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, + { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, + { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, + { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, + { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, + { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, + { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, + { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, + { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, +] From 3178a1ca9706b8bc0b6066a4eb40d63e959108a5 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 16:54:45 -0600 Subject: [PATCH 116/134] chore: replace pysdl2 with PySDL3 for SDL3 sensor support SDL3 includes a dedicated 8BitDo HIDAPI driver with native gyro/accel sensor support, which SDL2 lacked. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- pyproject.toml | 2 +- uv.lock | 1088 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 1082 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2fcf4e5..9f8a3df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ requires-python = ">=3.9" authors = [{ name = "Switch Pico Maintainers" }] dependencies = [ "pyserial", - "pysdl2", + "PySDL3", "rich", ] diff --git a/uv.lock b/uv.lock index 55858e0..c8f27d1 100644 --- a/uv.lock +++ b/uv.lock @@ -1,11 +1,465 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.9" resolution-markers = [ "python_full_version >= '3.10'", "python_full_version < '3.10'", ] +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl", version = "1.22.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "yarl", version = "1.23.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950, upload-time = "2026-01-03T17:29:13.002Z" }, + { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099, upload-time = "2026-01-03T17:29:15.268Z" }, + { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072, upload-time = "2026-01-03T17:29:16.922Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588, upload-time = "2026-01-03T17:29:18.539Z" }, + { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334, upload-time = "2026-01-03T17:29:21.028Z" }, + { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656, upload-time = "2026-01-03T17:29:22.531Z" }, + { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625, upload-time = "2026-01-03T17:29:24.276Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604, upload-time = "2026-01-03T17:29:26.099Z" }, + { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370, upload-time = "2026-01-03T17:29:28.121Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023, upload-time = "2026-01-03T17:29:30.002Z" }, + { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680, upload-time = "2026-01-03T17:29:31.782Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407, upload-time = "2026-01-03T17:29:33.392Z" }, + { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047, upload-time = "2026-01-03T17:29:34.855Z" }, + { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264, upload-time = "2026-01-03T17:29:36.389Z" }, + { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275, upload-time = "2026-01-03T17:29:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053, upload-time = "2026-01-03T17:29:40.074Z" }, + { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687, upload-time = "2026-01-03T17:29:41.819Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4c/a164164834f03924d9a29dc3acd9e7ee58f95857e0b467f6d04298594ebb/aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b", size = 746051, upload-time = "2026-01-03T17:29:43.287Z" }, + { url = "https://files.pythonhosted.org/packages/82/71/d5c31390d18d4f58115037c432b7e0348c60f6f53b727cad33172144a112/aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64", size = 499234, upload-time = "2026-01-03T17:29:44.822Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c9/741f8ac91e14b1d2e7100690425a5b2b919a87a5075406582991fb7de920/aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea", size = 494979, upload-time = "2026-01-03T17:29:46.405Z" }, + { url = "https://files.pythonhosted.org/packages/75/b5/31d4d2e802dfd59f74ed47eba48869c1c21552c586d5e81a9d0d5c2ad640/aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a", size = 1748297, upload-time = "2026-01-03T17:29:48.083Z" }, + { url = "https://files.pythonhosted.org/packages/1a/3e/eefad0ad42959f226bb79664826883f2687d602a9ae2941a18e0484a74d3/aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540", size = 1707172, upload-time = "2026-01-03T17:29:49.648Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3a/54a64299fac2891c346cdcf2aa6803f994a2e4beeaf2e5a09dcc54acc842/aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b", size = 1805405, upload-time = "2026-01-03T17:29:51.244Z" }, + { url = "https://files.pythonhosted.org/packages/6c/70/ddc1b7169cf64075e864f64595a14b147a895a868394a48f6a8031979038/aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3", size = 1899449, upload-time = "2026-01-03T17:29:53.938Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7e/6815aab7d3a56610891c76ef79095677b8b5be6646aaf00f69b221765021/aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1", size = 1748444, upload-time = "2026-01-03T17:29:55.484Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f2/073b145c4100da5511f457dc0f7558e99b2987cf72600d42b559db856fbc/aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3", size = 1606038, upload-time = "2026-01-03T17:29:57.179Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c1/778d011920cae03ae01424ec202c513dc69243cf2db303965615b81deeea/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440", size = 1724156, upload-time = "2026-01-03T17:29:58.914Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cb/3419eabf4ec1e9ec6f242c32b689248365a1cf621891f6f0386632525494/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7", size = 1722340, upload-time = "2026-01-03T17:30:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/7a/e5/76cf77bdbc435bf233c1f114edad39ed4177ccbfab7c329482b179cff4f4/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c", size = 1783041, upload-time = "2026-01-03T17:30:03.609Z" }, + { url = "https://files.pythonhosted.org/packages/9d/d4/dd1ca234c794fd29c057ce8c0566b8ef7fd6a51069de5f06fa84b9a1971c/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51", size = 1596024, upload-time = "2026-01-03T17:30:05.132Z" }, + { url = "https://files.pythonhosted.org/packages/55/58/4345b5f26661a6180afa686c473620c30a66afdf120ed3dd545bbc809e85/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4", size = 1804590, upload-time = "2026-01-03T17:30:07.135Z" }, + { url = "https://files.pythonhosted.org/packages/7b/06/05950619af6c2df7e0a431d889ba2813c9f0129cec76f663e547a5ad56f2/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29", size = 1740355, upload-time = "2026-01-03T17:30:09.083Z" }, + { url = "https://files.pythonhosted.org/packages/3e/80/958f16de79ba0422d7c1e284b2abd0c84bc03394fbe631d0a39ffa10e1eb/aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239", size = 433701, upload-time = "2026-01-03T17:30:10.869Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f2/27cdf04c9851712d6c1b99df6821a6623c3c9e55956d4b1e318c337b5a48/aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f", size = 457678, upload-time = "2026-01-03T17:30:12.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, + { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, + { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, + { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, + { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, + { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, + { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, + { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, + { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, + { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, + { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, + { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, + { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, + { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, + { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, + { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, + { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, + { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, + { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, + { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, + { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, + { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, + { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, + { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, + { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, + { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, + { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, + { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, + { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, + { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, + { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, + { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, + { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, + { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, + { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, + { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, + { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, + { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, + { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, + { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, + { url = "https://files.pythonhosted.org/packages/bf/79/446655656861d3e7e2c32bfcf160c7aa9e9dc63776a691b124dba65cdd77/aiohttp-3.13.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31a83ea4aead760dfcb6962efb1d861db48c34379f2ff72db9ddddd4cda9ea2e", size = 741433, upload-time = "2026-01-03T17:32:26.453Z" }, + { url = "https://files.pythonhosted.org/packages/cb/49/773c4b310b5140d2fb5e79bb0bf40b7b41dad80a288ca1a8759f5f72bda9/aiohttp-3.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:988a8c5e317544fdf0d39871559e67b6341065b87fceac641108c2096d5506b7", size = 497332, upload-time = "2026-01-03T17:32:28.37Z" }, + { url = "https://files.pythonhosted.org/packages/bc/31/1dcbc4b83a4e6f76a0ad883f07f21ffbfe29750c89db97381701508c9f45/aiohttp-3.13.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b174f267b5cfb9a7dba9ee6859cecd234e9a681841eb85068059bc867fb8f02", size = 492365, upload-time = "2026-01-03T17:32:30.234Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b5/b50657496c8754482cd7964e50aaf3aa84b3db61ed45daec4c1aec5b94b4/aiohttp-3.13.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:947c26539750deeaee933b000fb6517cc770bbd064bad6033f1cff4803881e43", size = 1660440, upload-time = "2026-01-03T17:32:32.586Z" }, + { url = "https://files.pythonhosted.org/packages/2a/73/9b69e5139d89d75127569298931444ad78ea86a5befd5599780b1e9a6880/aiohttp-3.13.3-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9ebf57d09e131f5323464bd347135a88622d1c0976e88ce15b670e7ad57e4bd6", size = 1632740, upload-time = "2026-01-03T17:32:34.793Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fe/3ea9b5af694b4e3aec0d0613a806132ca744747146fca68e96bf056f61a7/aiohttp-3.13.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4ae5b5a0e1926e504c81c5b84353e7a5516d8778fbbff00429fe7b05bb25cbce", size = 1719782, upload-time = "2026-01-03T17:32:37.737Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c2/46b3b06e60851cbb71efb0f79a3267279cbef7b12c58e68a1e897f269cca/aiohttp-3.13.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ba0eea45eb5cc3172dbfc497c066f19c41bac70963ea1a67d51fc92e4cf9a80", size = 1813527, upload-time = "2026-01-03T17:32:39.973Z" }, + { url = "https://files.pythonhosted.org/packages/36/23/71ceb78c769ed65fe4c697692de232b63dab399210678d2b00961ccb0619/aiohttp-3.13.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bae5c2ed2eae26cc382020edad80d01f36cb8e746da40b292e68fec40421dc6a", size = 1661268, upload-time = "2026-01-03T17:32:42.082Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8d/86e929523d955e85ebab7c0e2b9e0cb63604cfc27dc3280e10d0063cf682/aiohttp-3.13.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a60e60746623925eab7d25823329941aee7242d559baa119ca2b253c88a7bd6", size = 1552742, upload-time = "2026-01-03T17:32:44.622Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ea/3f5987cba1bab6bd151f0d97aa60f0ce04d3c83316692a6bb6ba2fb69f92/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e50a2e1404f063427c9d027378472316201a2290959a295169bcf25992d04558", size = 1632918, upload-time = "2026-01-03T17:32:46.749Z" }, + { url = "https://files.pythonhosted.org/packages/be/2c/7e1e85121f2e31ee938cb83a8f32dfafd4908530c10fabd6d46761c12ac7/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:9a9dc347e5a3dc7dfdbc1f82da0ef29e388ddb2ed281bfce9dd8248a313e62b7", size = 1644446, upload-time = "2026-01-03T17:32:49.063Z" }, + { url = "https://files.pythonhosted.org/packages/5d/35/ce6133d423ad0e8ca976a7c848f7146bca3520eea4ccf6b95e2d077c9d20/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b46020d11d23fe16551466c77823df9cc2f2c1e63cc965daf67fa5eec6ca1877", size = 1689487, upload-time = "2026-01-03T17:32:51.113Z" }, + { url = "https://files.pythonhosted.org/packages/50/f7/ff7a27c15603d460fd1366b3c22054f7ae4fa9310aca40b43bde35867fcd/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:69c56fbc1993fa17043e24a546959c0178fe2b5782405ad4559e6c13975c15e3", size = 1540715, upload-time = "2026-01-03T17:32:53.38Z" }, + { url = "https://files.pythonhosted.org/packages/17/02/053f11346e5b962e6d8a1c4f8c70c29d5970a1b4b8e7894c68e12c27a57f/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b99281b0704c103d4e11e72a76f1b543d4946fea7dd10767e7e1b5f00d4e5704", size = 1711835, upload-time = "2026-01-03T17:32:56.088Z" }, + { url = "https://files.pythonhosted.org/packages/fb/71/9b9761ddf276fd6708d13720197cbac19b8d67ecfa9116777924056cfcaa/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:40c5e40ecc29ba010656c18052b877a1c28f84344825efa106705e835c28530f", size = 1649593, upload-time = "2026-01-03T17:32:58.181Z" }, + { url = "https://files.pythonhosted.org/packages/ae/72/5d817e9ea218acae12a5e3b9ad1178cf0c12fc3570c0b47eea2daf95f9ea/aiohttp-3.13.3-cp39-cp39-win32.whl", hash = "sha256:56339a36b9f1fc708260c76c87e593e2afb30d26de9ae1eb445b5e051b98a7a1", size = 434831, upload-time = "2026-01-03T17:33:00.577Z" }, + { url = "https://files.pythonhosted.org/packages/39/cb/22659d9bf3149b7a2927bc2769cc9c8f8f5a80eba098398e03c199a43a85/aiohttp-3.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:c6b8568a3bb5819a0ad087f16d40e5a3fb6099f39ea1d5625a3edc1e923fc538", size = 457697, upload-time = "2026-01-03T17:33:03.167Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + +[[package]] +name = "attrs" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/8c/2c56124c6dc53a774d435f985b5973bc592f42d437be58c0c92d65ae7296/charset_normalizer-3.4.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95", size = 298751, upload-time = "2026-03-15T18:50:00.003Z" }, + { url = "https://files.pythonhosted.org/packages/86/2a/2a7db6b314b966a3bcad8c731c0719c60b931b931de7ae9f34b2839289ee/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd", size = 200027, upload-time = "2026-03-15T18:50:01.702Z" }, + { url = "https://files.pythonhosted.org/packages/68/f2/0fe775c74ae25e2a3b07b01538fc162737b3e3f795bada3bc26f4d4d495c/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4", size = 220741, upload-time = "2026-03-15T18:50:03.194Z" }, + { url = "https://files.pythonhosted.org/packages/10/98/8085596e41f00b27dd6aa1e68413d1ddda7e605f34dd546833c61fddd709/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db", size = 215802, upload-time = "2026-03-15T18:50:05.859Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ce/865e4e09b041bad659d682bbd98b47fb490b8e124f9398c9448065f64fee/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89", size = 207908, upload-time = "2026-03-15T18:50:07.676Z" }, + { url = "https://files.pythonhosted.org/packages/a8/54/8c757f1f7349262898c2f169e0d562b39dcb977503f18fdf0814e923db78/charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565", size = 194357, upload-time = "2026-03-15T18:50:09.327Z" }, + { url = "https://files.pythonhosted.org/packages/6f/29/e88f2fac9218907fc7a70722b393d1bbe8334c61fe9c46640dba349b6e66/charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9", size = 205610, upload-time = "2026-03-15T18:50:10.732Z" }, + { url = "https://files.pythonhosted.org/packages/4c/c5/21d7bb0cb415287178450171d130bed9d664211fdd59731ed2c34267b07d/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7", size = 203512, upload-time = "2026-03-15T18:50:12.535Z" }, + { url = "https://files.pythonhosted.org/packages/a4/be/ce52f3c7fdb35cc987ad38a53ebcef52eec498f4fb6c66ecfe62cfe57ba2/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550", size = 195398, upload-time = "2026-03-15T18:50:14.236Z" }, + { url = "https://files.pythonhosted.org/packages/81/a0/3ab5dd39d4859a3555e5dadfc8a9fa7f8352f8c183d1a65c90264517da0e/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0", size = 221772, upload-time = "2026-03-15T18:50:15.581Z" }, + { url = "https://files.pythonhosted.org/packages/04/6e/6a4e41a97ba6b2fa87f849c41e4d229449a586be85053c4d90135fe82d26/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8", size = 205759, upload-time = "2026-03-15T18:50:17.047Z" }, + { url = "https://files.pythonhosted.org/packages/db/3b/34a712a5ee64a6957bf355b01dc17b12de457638d436fdb05d01e463cd1c/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0", size = 216938, upload-time = "2026-03-15T18:50:18.44Z" }, + { url = "https://files.pythonhosted.org/packages/cb/05/5bd1e12da9ab18790af05c61aafd01a60f489778179b621ac2a305243c62/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b", size = 210138, upload-time = "2026-03-15T18:50:19.852Z" }, + { url = "https://files.pythonhosted.org/packages/bd/8e/3cb9e2d998ff6b21c0a1860343cb7b83eba9cdb66b91410e18fc4969d6ab/charset_normalizer-3.4.6-cp310-cp310-win32.whl", hash = "sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557", size = 144137, upload-time = "2026-03-15T18:50:21.505Z" }, + { url = "https://files.pythonhosted.org/packages/d8/8f/78f5489ffadb0db3eb7aff53d31c24531d33eb545f0c6f6567c25f49a5ff/charset_normalizer-3.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6", size = 154244, upload-time = "2026-03-15T18:50:22.81Z" }, + { url = "https://files.pythonhosted.org/packages/e4/74/e472659dffb0cadb2f411282d2d76c60da1fc94076d7fffed4ae8a93ec01/charset_normalizer-3.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058", size = 143312, upload-time = "2026-03-15T18:50:24.074Z" }, + { url = "https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e", size = 293582, upload-time = "2026-03-15T18:50:25.454Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b7/b1a117e5385cbdb3205f6055403c2a2a220c5ea80b8716c324eaf75c5c95/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9", size = 197240, upload-time = "2026-03-15T18:50:27.196Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5f/2574f0f09f3c3bc1b2f992e20bce6546cb1f17e111c5be07308dc5427956/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d", size = 217363, upload-time = "2026-03-15T18:50:28.601Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d1/0ae20ad77bc949ddd39b51bf383b6ca932f2916074c95cad34ae465ab71f/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de", size = 212994, upload-time = "2026-03-15T18:50:30.102Z" }, + { url = "https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73", size = 204697, upload-time = "2026-03-15T18:50:31.654Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/8a18fc411f085b82303cfb7154eed5bd49c77035eb7608d049468b53f87c/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c", size = 191673, upload-time = "2026-03-15T18:50:33.433Z" }, + { url = "https://files.pythonhosted.org/packages/ff/a7/11cfe61d6c5c5c7438d6ba40919d0306ed83c9ab957f3d4da2277ff67836/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc", size = 201120, upload-time = "2026-03-15T18:50:35.105Z" }, + { url = "https://files.pythonhosted.org/packages/b5/10/cf491fa1abd47c02f69687046b896c950b92b6cd7337a27e6548adbec8e4/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f", size = 200911, upload-time = "2026-03-15T18:50:36.819Z" }, + { url = "https://files.pythonhosted.org/packages/28/70/039796160b48b18ed466fde0af84c1b090c4e288fae26cd674ad04a2d703/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef", size = 192516, upload-time = "2026-03-15T18:50:38.228Z" }, + { url = "https://files.pythonhosted.org/packages/ff/34/c56f3223393d6ff3124b9e78f7de738047c2d6bc40a4f16ac0c9d7a1cb3c/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398", size = 218795, upload-time = "2026-03-15T18:50:39.664Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3b/ce2d4f86c5282191a041fdc5a4ce18f1c6bd40a5bd1f74cf8625f08d51c1/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e", size = 201833, upload-time = "2026-03-15T18:50:41.552Z" }, + { url = "https://files.pythonhosted.org/packages/3b/9b/b6a9f76b0fd7c5b5ec58b228ff7e85095370282150f0bd50b3126f5506d6/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed", size = 213920, upload-time = "2026-03-15T18:50:43.33Z" }, + { url = "https://files.pythonhosted.org/packages/ae/98/7bc23513a33d8172365ed30ee3a3b3fe1ece14a395e5fc94129541fc6003/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021", size = 206951, upload-time = "2026-03-15T18:50:44.789Z" }, + { url = "https://files.pythonhosted.org/packages/32/73/c0b86f3d1458468e11aec870e6b3feac931facbe105a894b552b0e518e79/charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e", size = 143703, upload-time = "2026-03-15T18:50:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4", size = 153857, upload-time = "2026-03-15T18:50:47.563Z" }, + { url = "https://files.pythonhosted.org/packages/e2/dc/9abe19c9b27e6cd3636036b9d1b387b78c40dedbf0b47f9366737684b4b0/charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316", size = 142751, upload-time = "2026-03-15T18:50:49.234Z" }, + { url = "https://files.pythonhosted.org/packages/e5/62/c0815c992c9545347aeea7859b50dc9044d147e2e7278329c6e02ac9a616/charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab", size = 295154, upload-time = "2026-03-15T18:50:50.88Z" }, + { url = "https://files.pythonhosted.org/packages/a8/37/bdca6613c2e3c58c7421891d80cc3efa1d32e882f7c4a7ee6039c3fc951a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21", size = 199191, upload-time = "2026-03-15T18:50:52.658Z" }, + { url = "https://files.pythonhosted.org/packages/6c/92/9934d1bbd69f7f398b38c5dae1cbf9cc672e7c34a4adf7b17c0a9c17d15d/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2", size = 218674, upload-time = "2026-03-15T18:50:54.102Z" }, + { url = "https://files.pythonhosted.org/packages/af/90/25f6ab406659286be929fd89ab0e78e38aa183fc374e03aa3c12d730af8a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff", size = 215259, upload-time = "2026-03-15T18:50:55.616Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ef/79a463eb0fff7f96afa04c1d4c51f8fc85426f918db467854bfb6a569ce3/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5", size = 207276, upload-time = "2026-03-15T18:50:57.054Z" }, + { url = "https://files.pythonhosted.org/packages/f7/72/d0426afec4b71dc159fa6b4e68f868cd5a3ecd918fec5813a15d292a7d10/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0", size = 195161, upload-time = "2026-03-15T18:50:58.686Z" }, + { url = "https://files.pythonhosted.org/packages/bf/18/c82b06a68bfcb6ce55e508225d210c7e6a4ea122bfc0748892f3dc4e8e11/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a", size = 203452, upload-time = "2026-03-15T18:51:00.196Z" }, + { url = "https://files.pythonhosted.org/packages/44/d6/0c25979b92f8adafdbb946160348d8d44aa60ce99afdc27df524379875cb/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2", size = 202272, upload-time = "2026-03-15T18:51:01.703Z" }, + { url = "https://files.pythonhosted.org/packages/2e/3d/7fea3e8fe84136bebbac715dd1221cc25c173c57a699c030ab9b8900cbb7/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5", size = 195622, upload-time = "2026-03-15T18:51:03.526Z" }, + { url = "https://files.pythonhosted.org/packages/57/8a/d6f7fd5cb96c58ef2f681424fbca01264461336d2a7fc875e4446b1f1346/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6", size = 220056, upload-time = "2026-03-15T18:51:05.269Z" }, + { url = "https://files.pythonhosted.org/packages/16/50/478cdda782c8c9c3fb5da3cc72dd7f331f031e7f1363a893cdd6ca0f8de0/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d", size = 203751, upload-time = "2026-03-15T18:51:06.858Z" }, + { url = "https://files.pythonhosted.org/packages/75/fc/cc2fcac943939c8e4d8791abfa139f685e5150cae9f94b60f12520feaa9b/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2", size = 216563, upload-time = "2026-03-15T18:51:08.564Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b7/a4add1d9a5f68f3d037261aecca83abdb0ab15960a3591d340e829b37298/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923", size = 209265, upload-time = "2026-03-15T18:51:10.312Z" }, + { url = "https://files.pythonhosted.org/packages/6c/18/c094561b5d64a24277707698e54b7f67bd17a4f857bbfbb1072bba07c8bf/charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4", size = 144229, upload-time = "2026-03-15T18:51:11.694Z" }, + { url = "https://files.pythonhosted.org/packages/ab/20/0567efb3a8fd481b8f34f739ebddc098ed062a59fed41a8d193a61939e8f/charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb", size = 154277, upload-time = "2026-03-15T18:51:13.004Z" }, + { url = "https://files.pythonhosted.org/packages/15/57/28d79b44b51933119e21f65479d0864a8d5893e494cf5daab15df0247c17/charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4", size = 142817, upload-time = "2026-03-15T18:51:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", size = 294823, upload-time = "2026-03-15T18:51:15.755Z" }, + { url = "https://files.pythonhosted.org/packages/47/7b/20e809b89c69d37be748d98e84dce6820bf663cf19cf6b942c951a3e8f41/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", size = 198527, upload-time = "2026-03-15T18:51:17.177Z" }, + { url = "https://files.pythonhosted.org/packages/37/a6/4f8d27527d59c039dce6f7622593cdcd3d70a8504d87d09eb11e9fdc6062/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf", size = 218388, upload-time = "2026-03-15T18:51:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/f6/9b/4770ccb3e491a9bacf1c46cc8b812214fe367c86a96353ccc6daf87b01ec/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8", size = 214563, upload-time = "2026-03-15T18:51:20.374Z" }, + { url = "https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", size = 206587, upload-time = "2026-03-15T18:51:21.807Z" }, + { url = "https://files.pythonhosted.org/packages/7e/70/3def227f1ec56f5c69dfc8392b8bd63b11a18ca8178d9211d7cc5e5e4f27/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88", size = 194724, upload-time = "2026-03-15T18:51:23.508Z" }, + { url = "https://files.pythonhosted.org/packages/58/ab/9318352e220c05efd31c2779a23b50969dc94b985a2efa643ed9077bfca5/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84", size = 202956, upload-time = "2026-03-15T18:51:25.239Z" }, + { url = "https://files.pythonhosted.org/packages/75/13/f3550a3ac25b70f87ac98c40d3199a8503676c2f1620efbf8d42095cfc40/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", size = 201923, upload-time = "2026-03-15T18:51:26.682Z" }, + { url = "https://files.pythonhosted.org/packages/1b/db/c5c643b912740b45e8eec21de1bbab8e7fc085944d37e1e709d3dcd9d72f/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c", size = 195366, upload-time = "2026-03-15T18:51:28.129Z" }, + { url = "https://files.pythonhosted.org/packages/5a/67/3b1c62744f9b2448443e0eb160d8b001c849ec3fef591e012eda6484787c/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194", size = 219752, upload-time = "2026-03-15T18:51:29.556Z" }, + { url = "https://files.pythonhosted.org/packages/f6/98/32ffbaf7f0366ffb0445930b87d103f6b406bc2c271563644bde8a2b1093/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc", size = 203296, upload-time = "2026-03-15T18:51:30.921Z" }, + { url = "https://files.pythonhosted.org/packages/41/12/5d308c1bbe60cabb0c5ef511574a647067e2a1f631bc8634fcafaccd8293/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f", size = 215956, upload-time = "2026-03-15T18:51:32.399Z" }, + { url = "https://files.pythonhosted.org/packages/53/e9/5f85f6c5e20669dbe56b165c67b0260547dea97dba7e187938833d791687/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", size = 208652, upload-time = "2026-03-15T18:51:34.214Z" }, + { url = "https://files.pythonhosted.org/packages/f1/11/897052ea6af56df3eef3ca94edafee410ca699ca0c7b87960ad19932c55e/charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d", size = 143940, upload-time = "2026-03-15T18:51:36.15Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389", size = 154101, upload-time = "2026-03-15T18:51:37.876Z" }, + { url = "https://files.pythonhosted.org/packages/01/a5/7abf15b4c0968e47020f9ca0935fb3274deb87cb288cd187cad92e8cdffd/charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f", size = 143109, upload-time = "2026-03-15T18:51:39.565Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, + { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, + { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, + { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, + { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, + { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, + { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, + { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, + { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, + { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, + { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, + { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, + { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, + { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, + { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, + { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, + { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, + { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, + { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, + { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, + { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, + { url = "https://files.pythonhosted.org/packages/41/85/580dbaa12ab31041ed7df59f0bebc8893514fc21da6c05c3a1c1707d118f/charset_normalizer-3.4.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31215157227939b4fb3d740cd23fe27be0439afef67b785a1eb78a3ae69cba9e", size = 298620, upload-time = "2026-03-15T18:52:57.332Z" }, + { url = "https://files.pythonhosted.org/packages/67/2c/1e55af3a5e2f52e44396d5c5b731e0ae4f3bb92915ff09a610fb2f4497eb/charset_normalizer-3.4.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecbbd45615a6885fe3240eb9db73b9e62518b611850fdf8ab08bd56de7ad2b17", size = 200106, upload-time = "2026-03-15T18:52:59.2Z" }, + { url = "https://files.pythonhosted.org/packages/10/42/0f2f51a1d16caa45fbf384fd337d4242df1a5b313babee211381d2d39a96/charset_normalizer-3.4.6-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c45a03a4c69820a399f1dda9e1d8fbf3562eda46e7720458180302021b08f778", size = 220539, upload-time = "2026-03-15T18:53:01.019Z" }, + { url = "https://files.pythonhosted.org/packages/1c/0c/4e10996c740eec0f4ae8afbbbfa25f66e8479c4b6ee9cff1ca366a4f6c04/charset_normalizer-3.4.6-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e8aeb10fcbe92767f0fa69ad5a72deca50d0dca07fbde97848997d778a50c9fe", size = 215821, upload-time = "2026-03-15T18:53:02.621Z" }, + { url = "https://files.pythonhosted.org/packages/46/73/205ae7644ebb581a7c6fa9c3751e283606e145f0e6f066003c66aafc9973/charset_normalizer-3.4.6-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54fae94be3d75f3e573c9a1b5402dc593de19377013c9a0e4285e3d402dd3a2a", size = 207917, upload-time = "2026-03-15T18:53:04.413Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ca/18f7dcf19afdab8097aeb2feb8b3809bb4b6ee356cb720abf5263d79406a/charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:2f7fdd9b6e6c529d6a2501a2d36b240109e78a8ceaef5687cfcfa2bbe671d297", size = 194513, upload-time = "2026-03-15T18:53:06.025Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6a/e7e3e204c8d79832a091e00b24595af1d5d9800d37dc1f67a6b264cc99a6/charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1d02209e06550bdaef34af58e041ad71b88e624f5d825519da3a3308e22687", size = 205612, upload-time = "2026-03-15T18:53:07.494Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ae/2169ebcea2851c5460c7a21993a0f87028be3c3e60899cb36251e1135cf5/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8bc5f0687d796c05b1e28ab0d38a50e6309906ee09375dd3aff6a9c09dd6e8f4", size = 203519, upload-time = "2026-03-15T18:53:09.048Z" }, + { url = "https://files.pythonhosted.org/packages/43/a0/6a49a925b9c225fe35dffeac5c76f68996b814c637e9d7213718f96be109/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ee4ec14bc1680d6b0afab9aea2ef27e26d2024f18b24a2d7155a52b60da7e833", size = 195411, upload-time = "2026-03-15T18:53:10.542Z" }, + { url = "https://files.pythonhosted.org/packages/47/f7/a26b0a18e52b1a0f11f53c2c400ed062f386ac227a64ae4be4c5a64699be/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d1a2ee9c1499fc8f86f4521f27a973c914b211ffa87322f4ee33bb35392da2c5", size = 221653, upload-time = "2026-03-15T18:53:12.394Z" }, + { url = "https://files.pythonhosted.org/packages/a7/3a/ed1d3b5bb55e3634bd5c31cedbe4fff79d0e5b8d9a062f663a757a07760d/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:48696db7f18afb80a068821504296eb0787d9ce239b91ca15059d1d3eaacf13b", size = 205650, upload-time = "2026-03-15T18:53:13.934Z" }, + { url = "https://files.pythonhosted.org/packages/b1/27/c75819eea5ceeefc49bae329327bb91e81adc346e2a9873d9fdb9e77cde6/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4f41da960b196ea355357285ad1316a00099f22d0929fe168343b99b254729c9", size = 216919, upload-time = "2026-03-15T18:53:15.44Z" }, + { url = "https://files.pythonhosted.org/packages/0f/42/6e91bf8b15f67b7c957091138a36057a083e60703cc27848d5e36ca1eb03/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:802168e03fba8bbc5ce0d866d589e4b1ca751d06edee69f7f3a19c5a9fe6b597", size = 210101, upload-time = "2026-03-15T18:53:17.045Z" }, + { url = "https://files.pythonhosted.org/packages/99/ff/101af2605e66a7ee59961d7f9e1060df7c92e8ea54208a02ab881422c24e/charset_normalizer-3.4.6-cp39-cp39-win32.whl", hash = "sha256:8761ac29b6c81574724322a554605608a9960769ea83d2c73e396f3df896ad54", size = 144136, upload-time = "2026-03-15T18:53:19.152Z" }, + { url = "https://files.pythonhosted.org/packages/1d/da/de5942dfbf21f28c19e9202267dabf7bc73f195465d020a3a60054520cc5/charset_normalizer-3.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:1cf0a70018692f85172348fe06d3a4b63f94ecb055e13a00c644d368eb82e5b8", size = 154210, upload-time = "2026-03-15T18:53:20.576Z" }, + { url = "https://files.pythonhosted.org/packages/06/df/1b780a25b86d22b1d736f6ac883afd38ffdf30ddc18e5dc0e82211f493f1/charset_normalizer-3.4.6-cp39-cp39-win_arm64.whl", hash = "sha256:3516bbb8d42169de9e61b8520cbeeeb716f12f4ecfe3fd30a9919aa16c806ca8", size = 143225, upload-time = "2026-03-15T18:53:22.072Z" }, + { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/c2/59/ae5cdac87a00962122ea37bb346d41b66aec05f9ce328fa2b9e216f8967b/frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47", size = 86967, upload-time = "2025-10-06T05:37:55.607Z" }, + { url = "https://files.pythonhosted.org/packages/8a/10/17059b2db5a032fd9323c41c39e9d1f5f9d0c8f04d1e4e3e788573086e61/frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca", size = 49984, upload-time = "2025-10-06T05:37:57.049Z" }, + { url = "https://files.pythonhosted.org/packages/4b/de/ad9d82ca8e5fa8f0c636e64606553c79e2b859ad253030b62a21fe9986f5/frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068", size = 50240, upload-time = "2025-10-06T05:37:58.145Z" }, + { url = "https://files.pythonhosted.org/packages/4e/45/3dfb7767c2a67d123650122b62ce13c731b6c745bc14424eea67678b508c/frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95", size = 219472, upload-time = "2025-10-06T05:37:59.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/bf/5bf23d913a741b960d5c1dac7c1985d8a2a1d015772b2d18ea168b08e7ff/frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459", size = 221531, upload-time = "2025-10-06T05:38:00.521Z" }, + { url = "https://files.pythonhosted.org/packages/d0/03/27ec393f3b55860859f4b74cdc8c2a4af3dbf3533305e8eacf48a4fd9a54/frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675", size = 219211, upload-time = "2025-10-06T05:38:01.842Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ad/0fd00c404fa73fe9b169429e9a972d5ed807973c40ab6b3cf9365a33d360/frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61", size = 231775, upload-time = "2025-10-06T05:38:03.384Z" }, + { url = "https://files.pythonhosted.org/packages/8a/c3/86962566154cb4d2995358bc8331bfc4ea19d07db1a96f64935a1607f2b6/frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6", size = 236631, upload-time = "2025-10-06T05:38:04.609Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/6ffad161dbd83782d2c66dc4d378a9103b31770cb1e67febf43aea42d202/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5", size = 218632, upload-time = "2025-10-06T05:38:05.917Z" }, + { url = "https://files.pythonhosted.org/packages/58/b2/4677eee46e0a97f9b30735e6ad0bf6aba3e497986066eb68807ac85cf60f/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3", size = 235967, upload-time = "2025-10-06T05:38:07.614Z" }, + { url = "https://files.pythonhosted.org/packages/05/f3/86e75f8639c5a93745ca7addbbc9de6af56aebb930d233512b17e46f6493/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1", size = 228799, upload-time = "2025-10-06T05:38:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/30/00/39aad3a7f0d98f5eb1d99a3c311215674ed87061aecee7851974b335c050/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178", size = 230566, upload-time = "2025-10-06T05:38:10.52Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4d/aa144cac44568d137846ddc4d5210fb5d9719eb1d7ec6fa2728a54b5b94a/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda", size = 217715, upload-time = "2025-10-06T05:38:11.832Z" }, + { url = "https://files.pythonhosted.org/packages/64/4c/8f665921667509d25a0dd72540513bc86b356c95541686f6442a3283019f/frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087", size = 39933, upload-time = "2025-10-06T05:38:13.061Z" }, + { url = "https://files.pythonhosted.org/packages/79/bd/bcc926f87027fad5e59926ff12d136e1082a115025d33c032d1cd69ab377/frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a", size = 44121, upload-time = "2025-10-06T05:38:14.572Z" }, + { url = "https://files.pythonhosted.org/packages/4c/07/9c2e4eb7584af4b705237b971b89a4155a8e57599c4483a131a39256a9a0/frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103", size = 40312, upload-time = "2025-10-06T05:38:15.699Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -45,6 +499,300 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/0b/19348d4c98980c4851d2f943f8ebafdece2ae7ef737adcfa5994ce8e5f10/multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", size = 77176, upload-time = "2026-01-26T02:42:59.784Z" }, + { url = "https://files.pythonhosted.org/packages/ef/04/9de3f8077852e3d438215c81e9b691244532d2e05b4270e89ce67b7d103c/multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", size = 44996, upload-time = "2026-01-26T02:43:01.674Z" }, + { url = "https://files.pythonhosted.org/packages/31/5c/08c7f7fe311f32e83f7621cd3f99d805f45519cd06fafb247628b861da7d/multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", size = 44631, upload-time = "2026-01-26T02:43:03.169Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7f/0e3b1390ae772f27501199996b94b52ceeb64fe6f9120a32c6c3f6b781be/multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", size = 242561, upload-time = "2026-01-26T02:43:04.733Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f4/8719f4f167586af317b69dd3e90f913416c91ca610cac79a45c53f590312/multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", size = 242223, upload-time = "2026-01-26T02:43:06.695Z" }, + { url = "https://files.pythonhosted.org/packages/47/ab/7c36164cce64a6ad19c6d9a85377b7178ecf3b89f8fd589c73381a5eedfd/multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", size = 222322, upload-time = "2026-01-26T02:43:08.472Z" }, + { url = "https://files.pythonhosted.org/packages/f5/79/a25add6fb38035b5337bc5734f296d9afc99163403bbcf56d4170f97eb62/multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", size = 254005, upload-time = "2026-01-26T02:43:10.127Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7b/64a87cf98e12f756fc8bd444b001232ffff2be37288f018ad0d3f0aae931/multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", size = 251173, upload-time = "2026-01-26T02:43:11.731Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ac/b605473de2bb404e742f2cc3583d12aedb2352a70e49ae8fce455b50c5aa/multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", size = 243273, upload-time = "2026-01-26T02:43:13.063Z" }, + { url = "https://files.pythonhosted.org/packages/03/65/11492d6a0e259783720f3bc1d9ea55579a76f1407e31ed44045c99542004/multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", size = 238956, upload-time = "2026-01-26T02:43:14.843Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a7/7ee591302af64e7c196fb63fe856c788993c1372df765102bd0448e7e165/multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", size = 233477, upload-time = "2026-01-26T02:43:16.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/99/c109962d58756c35fd9992fed7f2355303846ea2ff054bb5f5e9d6b888de/multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", size = 243615, upload-time = "2026-01-26T02:43:17.84Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5f/1973e7c771c86e93dcfe1c9cc55a5481b610f6614acfc28c0d326fe6bfad/multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", size = 249930, upload-time = "2026-01-26T02:43:19.06Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a5/f170fc2268c3243853580203378cd522446b2df632061e0a5409817854c7/multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", size = 243807, upload-time = "2026-01-26T02:43:20.286Z" }, + { url = "https://files.pythonhosted.org/packages/de/01/73856fab6d125e5bc652c3986b90e8699a95e84b48d72f39ade6c0e74a8c/multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", size = 239103, upload-time = "2026-01-26T02:43:21.508Z" }, + { url = "https://files.pythonhosted.org/packages/e7/46/f1220bd9944d8aa40d8ccff100eeeee19b505b857b6f603d6078cb5315b0/multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", size = 41416, upload-time = "2026-01-26T02:43:22.703Z" }, + { url = "https://files.pythonhosted.org/packages/68/00/9b38e272a770303692fc406c36e1a4c740f401522d5787691eb38a8925a8/multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", size = 46022, upload-time = "2026-01-26T02:43:23.77Z" }, + { url = "https://files.pythonhosted.org/packages/64/65/d8d42490c02ee07b6bbe00f7190d70bb4738b3cce7629aaf9f213ef730dd/multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", size = 43238, upload-time = "2026-01-26T02:43:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z" }, + { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ee/74525ebe3eb5fddcd6735fc03cbea3feeed4122b53bc798ac32d297ac9ae/multidict-6.7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:65573858d27cdeaca41893185677dc82395159aa28875a8867af66532d413a8f", size = 77107, upload-time = "2026-01-26T02:46:12.608Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9a/ce8744e777a74b3050b1bf56be3eed1053b3457302ea055f1ea437200a23/multidict-6.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c524c6fb8fc342793708ab111c4dbc90ff9abd568de220432500e47e990c0358", size = 44943, upload-time = "2026-01-26T02:46:14.016Z" }, + { url = "https://files.pythonhosted.org/packages/83/9c/1d2a283d9c6f31e260cb6c2fccadc3edcf6c4c14ee0929cd2af4d2606dd7/multidict-6.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aa23b001d968faef416ff70dc0f1ab045517b9b42a90edd3e9bcdb06479e31d5", size = 44603, upload-time = "2026-01-26T02:46:15.391Z" }, + { url = "https://files.pythonhosted.org/packages/87/9d/3b186201671583d8e8d6d79c07481a5aafd0ba7575e3d8566baec80c1e82/multidict-6.7.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6704fa2b7453b2fb121740555fa1ee20cd98c4d011120caf4d2b8d4e7c76eec0", size = 240573, upload-time = "2026-01-26T02:46:16.783Z" }, + { url = "https://files.pythonhosted.org/packages/42/7d/a52f5d4d0754311d1ac78478e34dff88de71259a8585e05ee14e5f877caf/multidict-6.7.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:121a34e5bfa410cdf2c8c49716de160de3b1dbcd86b49656f5681e4543bcd1a8", size = 240106, upload-time = "2026-01-26T02:46:18.432Z" }, + { url = "https://files.pythonhosted.org/packages/84/9f/d80118e6c30ff55b7d171bdc5520aad4b9626e657520b8d7c8ca8c2fad12/multidict-6.7.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:026d264228bcd637d4e060844e39cdc60f86c479e463d49075dedc21b18fbbe0", size = 219418, upload-time = "2026-01-26T02:46:20.526Z" }, + { url = "https://files.pythonhosted.org/packages/c7/bd/896e60b3457f194de77c7de64f9acce9f75da0518a5230ce1df534f6747b/multidict-6.7.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e697826df7eb63418ee190fd06ce9f1803593bb4b9517d08c60d9b9a7f69d8f", size = 252124, upload-time = "2026-01-26T02:46:22.157Z" }, + { url = "https://files.pythonhosted.org/packages/f4/de/ba6b30447c36a37078d0ba604aa12c1a52887af0c355236ca6e0a9d5286f/multidict-6.7.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb08271280173720e9fea9ede98e5231defcbad90f1624bea26f32ec8a956e2f", size = 249402, upload-time = "2026-01-26T02:46:23.718Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b2/50a383c96230e432895a2fd3bcfe1b65785899598259d871d5de6b93180c/multidict-6.7.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6b3228e1d80af737b72925ce5fb4daf5a335e49cd7ab77ed7b9fdfbf58c526e", size = 240346, upload-time = "2026-01-26T02:46:25.393Z" }, + { url = "https://files.pythonhosted.org/packages/89/37/16d391fd8da544b1489306e38a46785fa41dd0f0ef766837ed7d4676dde0/multidict-6.7.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3943debf0fbb57bdde5901695c11094a9a36723e5c03875f87718ee15ca2f4d2", size = 237010, upload-time = "2026-01-26T02:46:27.408Z" }, + { url = "https://files.pythonhosted.org/packages/b0/24/3152ee026eda86d5d3e3685182911e6951af7a016579da931080ce6ac9ad/multidict-6.7.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:98c5787b0a0d9a41d9311eae44c3b76e6753def8d8870ab501320efe75a6a5f8", size = 232018, upload-time = "2026-01-26T02:46:29.941Z" }, + { url = "https://files.pythonhosted.org/packages/9c/1f/48d3c27a72be7fd23a55d8847193c459959bf35a5bb5844530dab00b739b/multidict-6.7.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:08ccb2a6dc72009093ebe7f3f073e5ec5964cba9a706fa94b1a1484039b87941", size = 241498, upload-time = "2026-01-26T02:46:32.052Z" }, + { url = "https://files.pythonhosted.org/packages/1a/45/413643ae2952d0decdf6c1250f86d08a43e143271441e81027e38d598bd7/multidict-6.7.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb351f72c26dc9abe338ca7294661aa22969ad8ffe7ef7d5541d19f368dc854a", size = 247957, upload-time = "2026-01-26T02:46:33.666Z" }, + { url = "https://files.pythonhosted.org/packages/50/f8/f1d0ac23df15e0470776388bdb261506f63af1f81d28bacb5e262d6e12b6/multidict-6.7.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ac1c665bad8b5d762f5f85ebe4d94130c26965f11de70c708c75671297c776de", size = 241651, upload-time = "2026-01-26T02:46:35.7Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c9/1a2a18f383cf129add66b6c36b75c3911a7ba95cf26cb141482de085cc12/multidict-6.7.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fa6609d0364f4f6f58351b4659a1f3e0e898ba2a8c5cac04cb2c7bc556b0bc5", size = 236371, upload-time = "2026-01-26T02:46:37.37Z" }, + { url = "https://files.pythonhosted.org/packages/bb/aa/77d87e3fca31325b87e0eb72d5fe9a7472dcb51391a42df7ac1f3842f6c0/multidict-6.7.1-cp39-cp39-win32.whl", hash = "sha256:6f77ce314a29263e67adadc7e7c1bc699fcb3a305059ab973d038f87caa42ed0", size = 41426, upload-time = "2026-01-26T02:46:39.026Z" }, + { url = "https://files.pythonhosted.org/packages/e3/b3/e8863e6a2da15a9d7e98976ff402e871b7352c76566df6c18d0378e0d9cf/multidict-6.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:f537b55778cd3cbee430abe3131255d3a78202e0f9ea7ffc6ada893a4bcaeea4", size = 46180, upload-time = "2026-01-26T02:46:40.422Z" }, + { url = "https://files.pythonhosted.org/packages/93/d3/dd4fa951ad5b5fa216bf30054d705683d13405eea7459833d78f31b74c9c/multidict-6.7.1-cp39-cp39-win_arm64.whl", hash = "sha256:749aa54f578f2e5f439538706a475aa844bfa8ef75854b1401e6e528e4937cf9", size = 43231, upload-time = "2026-01-26T02:46:41.945Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, + { url = "https://files.pythonhosted.org/packages/9b/01/0ebaec9003f5d619a7475165961f8e3083cf8644d704b60395df3601632d/propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff", size = 80277, upload-time = "2025-10-08T19:48:36.647Z" }, + { url = "https://files.pythonhosted.org/packages/34/58/04af97ac586b4ef6b9026c3fd36ee7798b737a832f5d3440a4280dcebd3a/propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb", size = 45865, upload-time = "2025-10-08T19:48:37.859Z" }, + { url = "https://files.pythonhosted.org/packages/7c/19/b65d98ae21384518b291d9939e24a8aeac4fdb5101b732576f8f7540e834/propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac", size = 47636, upload-time = "2025-10-08T19:48:39.038Z" }, + { url = "https://files.pythonhosted.org/packages/b3/0f/317048c6d91c356c7154dca5af019e6effeb7ee15fa6a6db327cc19e12b4/propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888", size = 201126, upload-time = "2025-10-08T19:48:40.774Z" }, + { url = "https://files.pythonhosted.org/packages/71/69/0b2a7a5a6ee83292b4b997dbd80549d8ce7d40b6397c1646c0d9495f5a85/propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc", size = 209837, upload-time = "2025-10-08T19:48:42.167Z" }, + { url = "https://files.pythonhosted.org/packages/a5/92/c699ac495a6698df6e497fc2de27af4b6ace10d8e76528357ce153722e45/propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a", size = 215578, upload-time = "2025-10-08T19:48:43.56Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ee/14de81c5eb02c0ee4f500b4e39c4e1bd0677c06e72379e6ab18923c773fc/propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88", size = 197187, upload-time = "2025-10-08T19:48:45.309Z" }, + { url = "https://files.pythonhosted.org/packages/1d/94/48dce9aaa6d8dd5a0859bad75158ec522546d4ac23f8e2f05fac469477dd/propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00", size = 193478, upload-time = "2025-10-08T19:48:47.743Z" }, + { url = "https://files.pythonhosted.org/packages/60/b5/0516b563e801e1ace212afde869a0596a0d7115eec0b12d296d75633fb29/propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0", size = 190650, upload-time = "2025-10-08T19:48:49.373Z" }, + { url = "https://files.pythonhosted.org/packages/24/89/e0f7d4a5978cd56f8cd67735f74052f257dc471ec901694e430f0d1572fe/propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e", size = 200251, upload-time = "2025-10-08T19:48:51.4Z" }, + { url = "https://files.pythonhosted.org/packages/06/7d/a1fac863d473876ed4406c914f2e14aa82d2f10dd207c9e16fc383cc5a24/propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781", size = 200919, upload-time = "2025-10-08T19:48:53.227Z" }, + { url = "https://files.pythonhosted.org/packages/c3/4e/f86a256ff24944cf5743e4e6c6994e3526f6acfcfb55e21694c2424f758c/propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183", size = 193211, upload-time = "2025-10-08T19:48:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/6e/3f/3fbad5f4356b068f1b047d300a6ff2c66614d7030f078cd50be3fec04228/propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19", size = 38314, upload-time = "2025-10-08T19:48:56.792Z" }, + { url = "https://files.pythonhosted.org/packages/a4/45/d78d136c3a3d215677abb886785aae744da2c3005bcb99e58640c56529b1/propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f", size = 41912, upload-time = "2025-10-08T19:48:57.995Z" }, + { url = "https://files.pythonhosted.org/packages/fc/2a/b0632941f25139f4e58450b307242951f7c2717a5704977c6d5323a800af/propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938", size = 38450, upload-time = "2025-10-08T19:48:59.349Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +] + [[package]] name = "pygments" version = "2.19.2" @@ -55,12 +803,17 @@ wheels = [ ] [[package]] -name = "pysdl2" -version = "0.9.17" +name = "pysdl3" +version = "0.9.11b0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/ff/8704d84ad4d25f0a7bf7912504f64575e432e8d57dfba2fe35f5b2db7e04/pysdl2-0.9.17.tar.gz", hash = "sha256:48c6ef01a4eb123db5f7e46e1a1b565675755b07e615f3fe20a623c94735b52b", size = 775955, upload-time = "2024-12-30T18:07:27.562Z" } +dependencies = [ + { name = "aiohttp" }, + { name = "packaging" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/2a/86e2b1cc7ac5a186ac3bad262cfbe55313268b6a8f7a3e61a4212fa4af09/pysdl3-0.9.11b0.tar.gz", hash = "sha256:e87dce397221c4943763bb45ec8725deb07b2bf5636d5da875e9d0ae34504068", size = 1421769, upload-time = "2026-03-15T19:28:04.222Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/e2/399ea7900e7510096aeb41439e6f1540bef0bdf7e15cc2d8464e4adb71e8/PySDL2-0.9.17-py3-none-any.whl", hash = "sha256:fe923dbf5c7b27bbc1eb2bf58abfa793f8f13fd7ae8b27b1bc2de49920bcbd41", size = 583137, upload-time = "2024-12-30T18:07:25.987Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ff/58a5b5a5bbf1fb822359c8fcf1a307d9dadc078d988cd6e770f4381081bf/pysdl3-0.9.11b0-py3-none-any.whl", hash = "sha256:98bd71dd4e1bcdaa5e83da200a208439048690da627b2d19163f5e222518cb24", size = 99667, upload-time = "2026-03-15T19:28:02.81Z" }, ] [[package]] @@ -72,6 +825,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" }, ] +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + [[package]] name = "rich" version = "14.3.3" @@ -91,14 +859,320 @@ name = "switch-pico-bridge" version = "0.1.0" source = { editable = "." } dependencies = [ - { name = "pysdl2" }, + { name = "pysdl3" }, { name = "pyserial" }, { name = "rich" }, ] [package.metadata] requires-dist = [ - { name = "pysdl2" }, + { name = "pysdl3" }, { name = "pyserial" }, { name = "rich" }, ] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "yarl" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "idna", marker = "python_full_version < '3.10'" }, + { name = "multidict", marker = "python_full_version < '3.10'" }, + { name = "propcache", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517, upload-time = "2025-10-06T14:08:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495, upload-time = "2025-10-06T14:08:46.2Z" }, + { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400, upload-time = "2025-10-06T14:08:47.855Z" }, + { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545, upload-time = "2025-10-06T14:08:49.683Z" }, + { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598, upload-time = "2025-10-06T14:08:51.215Z" }, + { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893, upload-time = "2025-10-06T14:08:53.144Z" }, + { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240, upload-time = "2025-10-06T14:08:55.036Z" }, + { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965, upload-time = "2025-10-06T14:08:56.722Z" }, + { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026, upload-time = "2025-10-06T14:08:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637, upload-time = "2025-10-06T14:09:00.506Z" }, + { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082, upload-time = "2025-10-06T14:09:01.936Z" }, + { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811, upload-time = "2025-10-06T14:09:03.445Z" }, + { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223, upload-time = "2025-10-06T14:09:05.401Z" }, + { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118, upload-time = "2025-10-06T14:09:11.148Z" }, + { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852, upload-time = "2025-10-06T14:09:12.958Z" }, + { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" }, + { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" }, + { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" }, + { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" }, + { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" }, + { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" }, + { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" }, + { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" }, + { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" }, + { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" }, + { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" }, + { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" }, + { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, + { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, + { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, + { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, + { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, + { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, + { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, + { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, + { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, + { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, + { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, + { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, + { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, + { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, + { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, + { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, + { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, + { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, + { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, + { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, + { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, + { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, + { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, + { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, + { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, + { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, + { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, + { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, + { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, + { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, + { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, + { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, + { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, + { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, + { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, + { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, + { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, + { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, + { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, + { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, + { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, + { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, + { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, + { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, + { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, + { url = "https://files.pythonhosted.org/packages/94/fd/6480106702a79bcceda5fd9c63cb19a04a6506bd5ce7fd8d9b63742f0021/yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748", size = 141301, upload-time = "2025-10-06T14:12:19.01Z" }, + { url = "https://files.pythonhosted.org/packages/42/e1/6d95d21b17a93e793e4ec420a925fe1f6a9342338ca7a563ed21129c0990/yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859", size = 93864, upload-time = "2025-10-06T14:12:21.05Z" }, + { url = "https://files.pythonhosted.org/packages/32/58/b8055273c203968e89808413ea4c984988b6649baabf10f4522e67c22d2f/yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9", size = 94706, upload-time = "2025-10-06T14:12:23.287Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/d7bfbc28a88c2895ecd0da6a874def0c147de78afc52c773c28e1aa233a3/yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054", size = 347100, upload-time = "2025-10-06T14:12:28.527Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e8/37a1e7b99721c0564b1fc7b0a4d1f595ef6fb8060d82ca61775b644185f7/yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b", size = 318902, upload-time = "2025-10-06T14:12:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ef/34724449d7ef2db4f22df644f2dac0b8a275d20f585e526937b3ae47b02d/yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60", size = 363302, upload-time = "2025-10-06T14:12:32.295Z" }, + { url = "https://files.pythonhosted.org/packages/8a/04/88a39a5dad39889f192cce8d66cc4c58dbeca983e83f9b6bf23822a7ed91/yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890", size = 370816, upload-time = "2025-10-06T14:12:34.01Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1f/5e895e547129413f56c76be2c3ce4b96c797d2d0ff3e16a817d9269b12e6/yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba", size = 346465, upload-time = "2025-10-06T14:12:35.977Z" }, + { url = "https://files.pythonhosted.org/packages/11/13/a750e9fd6f9cc9ed3a52a70fe58ffe505322f0efe0d48e1fd9ffe53281f5/yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca", size = 341506, upload-time = "2025-10-06T14:12:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/3c/67/bb6024de76e7186611ebe626aec5b71a2d2ecf9453e795f2dbd80614784c/yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba", size = 335030, upload-time = "2025-10-06T14:12:39.775Z" }, + { url = "https://files.pythonhosted.org/packages/a2/be/50b38447fd94a7992996a62b8b463d0579323fcfc08c61bdba949eef8a5d/yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b", size = 358560, upload-time = "2025-10-06T14:12:41.547Z" }, + { url = "https://files.pythonhosted.org/packages/e2/89/c020b6f547578c4e3dbb6335bf918f26e2f34ad0d1e515d72fd33ac0c635/yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e", size = 357290, upload-time = "2025-10-06T14:12:43.861Z" }, + { url = "https://files.pythonhosted.org/packages/8c/52/c49a619ee35a402fa3a7019a4fa8d26878fec0d1243f6968bbf516789578/yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8", size = 350700, upload-time = "2025-10-06T14:12:46.868Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c9/f5042d87777bf6968435f04a2bbb15466b2f142e6e47fa4f34d1a3f32f0c/yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b", size = 82323, upload-time = "2025-10-06T14:12:48.633Z" }, + { url = "https://files.pythonhosted.org/packages/fd/58/d00f7cad9eba20c4eefac2682f34661d1d1b3a942fc0092eb60e78cfb733/yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed", size = 87145, upload-time = "2025-10-06T14:12:50.241Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a3/70904f365080780d38b919edd42d224b8c4ce224a86950d2eaa2a24366ad/yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2", size = 82173, upload-time = "2025-10-06T14:12:51.869Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, +] + +[[package]] +name = "yarl" +version = "1.23.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +dependencies = [ + { name = "idna", marker = "python_full_version >= '3.10'" }, + { name = "multidict", marker = "python_full_version >= '3.10'" }, + { name = "propcache", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/0d/9cc638702f6fc3c7a3685bcc8cf2a9ed7d6206e932a49f5242658047ef51/yarl-1.23.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cff6d44cb13d39db2663a22b22305d10855efa0fa8015ddeacc40bc59b9d8107", size = 123764, upload-time = "2026-03-01T22:04:09.7Z" }, + { url = "https://files.pythonhosted.org/packages/7a/35/5a553687c5793df5429cd1db45909d4f3af7eee90014888c208d086a44f0/yarl-1.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c53f8347cd4200f0d70a48ad059cabaf24f5adc6ba08622a23423bc7efa10d", size = 86282, upload-time = "2026-03-01T22:04:11.892Z" }, + { url = "https://files.pythonhosted.org/packages/68/2e/c5a2234238f8ce37a8312b52801ee74117f576b1539eec8404a480434acc/yarl-1.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a6940a074fb3c48356ed0158a3ca5699c955ee4185b4d7d619be3c327143e05", size = 86053, upload-time = "2026-03-01T22:04:13.292Z" }, + { url = "https://files.pythonhosted.org/packages/74/3f/bbd8ff36fb038622797ffbaf7db314918bb4d76f1cc8a4f9ca7a55fe5195/yarl-1.23.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed5f69ce7be7902e5c70ea19eb72d20abf7d725ab5d49777d696e32d4fc1811d", size = 99395, upload-time = "2026-03-01T22:04:15.133Z" }, + { url = "https://files.pythonhosted.org/packages/77/04/9516bc4e269d2a3ec9c6779fcdeac51ce5b3a9b0156f06ac7152e5bba864/yarl-1.23.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:389871e65468400d6283c0308e791a640b5ab5c83bcee02a2f51295f95e09748", size = 92143, upload-time = "2026-03-01T22:04:16.829Z" }, + { url = "https://files.pythonhosted.org/packages/c7/63/88802d1f6b1cb1fc67d67a58cd0cf8a1790de4ce7946e434240f1d60ab4a/yarl-1.23.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dda608c88cf709b1d406bdfcd84d8d63cff7c9e577a403c6108ce8ce9dcc8764", size = 107643, upload-time = "2026-03-01T22:04:18.519Z" }, + { url = "https://files.pythonhosted.org/packages/8e/db/4f9b838f4d8bdd6f0f385aed8bbf21c71ed11a0b9983305c302cbd557815/yarl-1.23.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c4fe09e0780c6c3bf2b7d4af02ee2394439d11a523bbcf095cf4747c2932007", size = 108700, upload-time = "2026-03-01T22:04:20.373Z" }, + { url = "https://files.pythonhosted.org/packages/50/12/95a1d33f04a79c402664070d43b8b9f72dc18914e135b345b611b0b1f8cc/yarl-1.23.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c9921eb8bd12633b41ad27686bbb0b1a2a9b8452bfdf221e34f311e9942ed4", size = 102769, upload-time = "2026-03-01T22:04:23.055Z" }, + { url = "https://files.pythonhosted.org/packages/86/65/91a0285f51321369fd1a8308aa19207520c5f0587772cfc2e03fc2467e90/yarl-1.23.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5f10fd85e4b75967468af655228fbfd212bdf66db1c0d135065ce288982eda26", size = 101114, upload-time = "2026-03-01T22:04:25.031Z" }, + { url = "https://files.pythonhosted.org/packages/58/80/c7c8244fc3e5bc483dc71a09560f43b619fab29301a0f0a8f936e42865c7/yarl-1.23.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dbf507e9ef5688bada447a24d68b4b58dd389ba93b7afc065a2ba892bea54769", size = 98883, upload-time = "2026-03-01T22:04:27.281Z" }, + { url = "https://files.pythonhosted.org/packages/86/e7/71ca9cc9ca79c0b7d491216177d1aed559d632947b8ffb0ee60f7d8b23e3/yarl-1.23.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:85e9beda1f591bc73e77ea1c51965c68e98dafd0fec72cdd745f77d727466716", size = 94172, upload-time = "2026-03-01T22:04:28.554Z" }, + { url = "https://files.pythonhosted.org/packages/6a/3f/6c6c8a0fe29c26fb2db2e8d32195bb84ec1bfb8f1d32e7f73b787fcf349b/yarl-1.23.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0e1fdaa14ef51366d7757b45bde294e95f6c8c049194e793eedb8387c86d5993", size = 107010, upload-time = "2026-03-01T22:04:30.385Z" }, + { url = "https://files.pythonhosted.org/packages/56/38/12730c05e5ad40a76374d440ed8b0899729a96c250516d91c620a6e38fc2/yarl-1.23.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:75e3026ab649bf48f9a10c0134512638725b521340293f202a69b567518d94e0", size = 100285, upload-time = "2026-03-01T22:04:31.752Z" }, + { url = "https://files.pythonhosted.org/packages/34/92/6a7be9239f2347234e027284e7a5f74b1140cc86575e7b469d13fba1ebfe/yarl-1.23.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:80e6d33a3d42a7549b409f199857b4fb54e2103fc44fb87605b6663b7a7ff750", size = 108230, upload-time = "2026-03-01T22:04:33.844Z" }, + { url = "https://files.pythonhosted.org/packages/5e/81/4aebccfa9376bd98b9d8bfad20621a57d3e8cfc5b8631c1fa5f62cdd03f4/yarl-1.23.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5ec2f42d41ccbd5df0270d7df31618a8ee267bfa50997f5d720ddba86c4a83a6", size = 103008, upload-time = "2026-03-01T22:04:35.856Z" }, + { url = "https://files.pythonhosted.org/packages/38/0f/0b4e3edcec794a86b853b0c6396c0a888d72dfce19b2d88c02ac289fb6c1/yarl-1.23.0-cp310-cp310-win32.whl", hash = "sha256:debe9c4f41c32990771be5c22b56f810659f9ddf3d63f67abfdcaa2c6c9c5c1d", size = 83073, upload-time = "2026-03-01T22:04:38.268Z" }, + { url = "https://files.pythonhosted.org/packages/a0/71/ad95c33da18897e4c636528bbc24a1dd23fe16797de8bc4ec667b8db0ba4/yarl-1.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f043cb8a2d71c981c09c510da013bc79fd661f5c60139f00dd3c3cc4f2ffb", size = 87328, upload-time = "2026-03-01T22:04:39.558Z" }, + { url = "https://files.pythonhosted.org/packages/e2/14/dfa369523c79bccf9c9c746b0a63eb31f65db9418ac01275f7950962e504/yarl-1.23.0-cp310-cp310-win_arm64.whl", hash = "sha256:263cd4f47159c09b8b685890af949195b51d1aa82ba451c5847ca9bc6413c220", size = 82463, upload-time = "2026-03-01T22:04:41.454Z" }, + { url = "https://files.pythonhosted.org/packages/a2/aa/60da938b8f0997ba3a911263c40d82b6f645a67902a490b46f3355e10fae/yarl-1.23.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99", size = 123641, upload-time = "2026-03-01T22:04:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c", size = 86248, upload-time = "2026-03-01T22:04:44.757Z" }, + { url = "https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432", size = 85988, upload-time = "2026-03-01T22:04:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6c/4a90d59c572e46b270ca132aca66954f1175abd691f74c1ef4c6711828e2/yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a", size = 100566, upload-time = "2026-03-01T22:04:47.639Z" }, + { url = "https://files.pythonhosted.org/packages/49/fb/c438fb5108047e629f6282a371e6e91cf3f97ee087c4fb748a1f32ceef55/yarl-1.23.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05", size = 92079, upload-time = "2026-03-01T22:04:48.925Z" }, + { url = "https://files.pythonhosted.org/packages/d9/13/d269aa1aed3e4f50a5a103f96327210cc5fa5dd2d50882778f13c7a14606/yarl-1.23.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83", size = 108741, upload-time = "2026-03-01T22:04:50.838Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/115b16f22c37ea4437d323e472945bea97301c8ec6089868fa560abab590/yarl-1.23.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c", size = 108099, upload-time = "2026-03-01T22:04:52.499Z" }, + { url = "https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598", size = 102678, upload-time = "2026-03-01T22:04:55.176Z" }, + { url = "https://files.pythonhosted.org/packages/85/59/cd98e556fbb2bf8fab29c1a722f67ad45c5f3447cac798ab85620d1e70af/yarl-1.23.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b", size = 100803, upload-time = "2026-03-01T22:04:56.588Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c0/b39770b56d4a9f0bb5f77e2f1763cd2d75cc2f6c0131e3b4c360348fcd65/yarl-1.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c", size = 100163, upload-time = "2026-03-01T22:04:58.492Z" }, + { url = "https://files.pythonhosted.org/packages/e7/64/6980f99ab00e1f0ff67cb84766c93d595b067eed07439cfccfc8fb28c1a6/yarl-1.23.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788", size = 93859, upload-time = "2026-03-01T22:05:00.268Z" }, + { url = "https://files.pythonhosted.org/packages/38/69/912e6c5e146793e5d4b5fe39ff5b00f4d22463dfd5a162bec565ac757673/yarl-1.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222", size = 108202, upload-time = "2026-03-01T22:05:02.273Z" }, + { url = "https://files.pythonhosted.org/packages/59/97/35ca6767524687ad64e5f5c31ad54bc76d585585a9fcb40f649e7e82ffed/yarl-1.23.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb", size = 99866, upload-time = "2026-03-01T22:05:03.597Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1c/1a3387ee6d73589f6f2a220ae06f2984f6c20b40c734989b0a44f5987308/yarl-1.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc", size = 107852, upload-time = "2026-03-01T22:05:04.986Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b8/35c0750fcd5a3f781058bfd954515dd4b1eab45e218cbb85cf11132215f1/yarl-1.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2", size = 102919, upload-time = "2026-03-01T22:05:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1c/9a1979aec4a81896d597bcb2177827f2dbee3f5b7cc48b2d0dadb644b41d/yarl-1.23.0-cp311-cp311-win32.whl", hash = "sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5", size = 82602, upload-time = "2026-03-01T22:05:08.444Z" }, + { url = "https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46", size = 87461, upload-time = "2026-03-01T22:05:10.145Z" }, + { url = "https://files.pythonhosted.org/packages/93/95/07e3553fe6f113e6864a20bdc53a78113cda3b9ced8784ee52a52c9f80d8/yarl-1.23.0-cp311-cp311-win_arm64.whl", hash = "sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928", size = 82336, upload-time = "2026-03-01T22:05:11.554Z" }, + { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, + { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, + { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, + { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, + { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, + { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, + { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, + { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, + { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, + { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, + { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, + { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, + { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, + { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, + { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, + { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, + { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, + { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, + { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, + { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, + { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, + { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, + { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, + { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, + { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, + { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, + { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, + { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, + { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, + { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, + { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, + { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, + { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, + { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, + { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, + { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, + { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, + { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, + { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, + { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, + { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, + { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, + { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, + { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, + { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, + { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, + { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, + { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, + { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, + { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, + { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, + { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, + { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, + { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, + { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, + { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, + { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, + { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, + { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, + { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, + { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, + { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, + { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, +] From 5c18c75d33c6726b89565d075d63522d89abd618 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 16:54:58 -0600 Subject: [PATCH 117/134] feat(bridge): migrate to SDL3 and fix controller hotplug issues - Migrate all SDL2 GameController API calls to SDL3 Gamepad equivalents - Fix PermissionError on controller reconnect by closing UART handle before releasing port back to pool (Windows holds COM ports exclusively) - Add DisplayIndexAllocator so controllers get stable display indices (0, 0, 0) instead of incrementing SDL instance IDs (1, 2, 3) on each reconnect - Button mapping now uses SDL3 positional constants (SOUTH/EAST/WEST/NORTH) which works correctly for all controller types without USE_BUTTON_LABELS hint Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- .../controller_uart_bridge.py | 399 ++++++++++-------- src/switch_pico_bridge/switch_pico_uart.py | 6 +- 2 files changed, 223 insertions(+), 182 deletions(-) diff --git a/src/switch_pico_bridge/controller_uart_bridge.py b/src/switch_pico_bridge/controller_uart_bridge.py index 7b280d3..a5a22af 100644 --- a/src/switch_pico_bridge/controller_uart_bridge.py +++ b/src/switch_pico_bridge/controller_uart_bridge.py @@ -16,6 +16,7 @@ Features inspired by ``host/controller_bridge.py``: from __future__ import annotations import argparse +import ctypes import os import sys import time @@ -26,8 +27,7 @@ from pathlib import Path from typing import Dict, List, Optional, Tuple from serial import SerialException -import sdl2 -import sdl2.ext +import sdl3 from rich.console import Console from rich.prompt import Prompt from rich.table import Table @@ -58,8 +58,8 @@ RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped aft RUMBLE_MIN_ACTIVE = 0.40 # below this, rumble is treated as off/noise RUMBLE_SCALE = 1.0 CONTROLLER_DB_URL_DEFAULT = "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt" -SDL_TRUE = getattr(sdl2, "SDL_TRUE", 1) -SDL_CONTROLLERSENSORUPDATE = getattr(sdl2, "SDL_CONTROLLERSENSORUPDATE", 0x658) +SDL_TRUE = True +SDL_EVENT_GAMEPAD_SENSOR_UPDATE = getattr(sdl3, "SDL_EVENT_GAMEPAD_SENSOR_UPDATE", 0x658) GYRO_BIAS_SAMPLES = 200 IMU_BUFFER_SIZE = 32 @@ -121,41 +121,41 @@ def parse_hotkey(value: str) -> str: def set_hint(name: str, value: str) -> None: - """Set an SDL hint safely even if the constant is missing in PySDL2.""" + """Set an SDL hint safely even if the constant is missing.""" try: - sdl2.SDL_SetHint(name.encode(), value.encode()) + sdl3.SDL_SetHint(name.encode(), value.encode()) except Exception: pass BUTTON_MAP = { # Direct mapping to the Switch PRO mask definitions in switch_pro_descriptors.h - sdl2.SDL_CONTROLLER_BUTTON_A: SwitchButton.A, - sdl2.SDL_CONTROLLER_BUTTON_B: SwitchButton.B, - sdl2.SDL_CONTROLLER_BUTTON_X: SwitchButton.X, - sdl2.SDL_CONTROLLER_BUTTON_Y: SwitchButton.Y, - sdl2.SDL_CONTROLLER_BUTTON_LEFTSHOULDER: SwitchButton.L, - sdl2.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: SwitchButton.R, - sdl2.SDL_CONTROLLER_BUTTON_BACK: SwitchButton.MINUS, - sdl2.SDL_CONTROLLER_BUTTON_START: SwitchButton.PLUS, - sdl2.SDL_CONTROLLER_BUTTON_GUIDE: SwitchButton.HOME, - sdl2.SDL_CONTROLLER_BUTTON_MISC1: SwitchButton.CAPTURE, - sdl2.SDL_CONTROLLER_BUTTON_LEFTSTICK: SwitchButton.LCLICK, - sdl2.SDL_CONTROLLER_BUTTON_RIGHTSTICK: SwitchButton.RCLICK, + sdl3.SDL_GAMEPAD_BUTTON_SOUTH: SwitchButton.B, + sdl3.SDL_GAMEPAD_BUTTON_EAST: SwitchButton.A, + sdl3.SDL_GAMEPAD_BUTTON_WEST: SwitchButton.Y, + sdl3.SDL_GAMEPAD_BUTTON_NORTH: SwitchButton.X, + sdl3.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: SwitchButton.L, + sdl3.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: SwitchButton.R, + sdl3.SDL_GAMEPAD_BUTTON_BACK: SwitchButton.MINUS, + sdl3.SDL_GAMEPAD_BUTTON_START: SwitchButton.PLUS, + sdl3.SDL_GAMEPAD_BUTTON_GUIDE: SwitchButton.HOME, + sdl3.SDL_GAMEPAD_BUTTON_MISC1: SwitchButton.CAPTURE, + sdl3.SDL_GAMEPAD_BUTTON_LEFT_STICK: SwitchButton.LCLICK, + sdl3.SDL_GAMEPAD_BUTTON_RIGHT_STICK: SwitchButton.RCLICK, } DPAD_BUTTONS = { - sdl2.SDL_CONTROLLER_BUTTON_DPAD_UP: "up", - sdl2.SDL_CONTROLLER_BUTTON_DPAD_DOWN: "down", - sdl2.SDL_CONTROLLER_BUTTON_DPAD_LEFT: "left", - sdl2.SDL_CONTROLLER_BUTTON_DPAD_RIGHT: "right", + sdl3.SDL_GAMEPAD_BUTTON_DPAD_UP: "up", + sdl3.SDL_GAMEPAD_BUTTON_DPAD_DOWN: "down", + sdl3.SDL_GAMEPAD_BUTTON_DPAD_LEFT: "left", + sdl3.SDL_GAMEPAD_BUTTON_DPAD_RIGHT: "right", } STICK_AXIS_LABELS = ( - (sdl2.SDL_CONTROLLER_AXIS_LEFTX, "LX"), - (sdl2.SDL_CONTROLLER_AXIS_LEFTY, "LY"), - (sdl2.SDL_CONTROLLER_AXIS_RIGHTX, "RX"), - (sdl2.SDL_CONTROLLER_AXIS_RIGHTY, "RY"), + (sdl3.SDL_GAMEPAD_AXIS_LEFTX, "LX"), + (sdl3.SDL_GAMEPAD_AXIS_LEFTY, "LY"), + (sdl3.SDL_GAMEPAD_AXIS_RIGHTX, "RX"), + (sdl3.SDL_GAMEPAD_AXIS_RIGHTY, "RY"), ) STICK_AXES = tuple(axis for axis, _ in STICK_AXIS_LABELS) @@ -200,26 +200,26 @@ def interactive_pairing( return mappings -def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> float: +def apply_rumble(controller: sdl3.SDL_Gamepad, payload: bytes) -> float: """Apply rumble payload to SDL controller and return max normalized energy.""" left_norm, right_norm = decode_rumble(payload) max_norm = max(left_norm, right_norm) # Treat small rumble as "off" to avoid idle buzz. if max_norm < RUMBLE_MIN_ACTIVE: - sdl2.SDL_GameControllerRumble(controller, 0, 0, 0) + sdl3.SDL_RumbleGamepad(controller, 0, 0, 0) return 0.0 # Attenuate to feel closer to a real controller; cap at ~25% strength. scale = RUMBLE_SCALE low = int(min(1.0, left_norm * scale) * 0xFFFF) # SDL: low_frequency_rumble high = int(min(1.0, right_norm * scale) * 0xFFFF) # SDL: high_frequency_rumble duration = 10 - sdl2.SDL_GameControllerRumble(controller, low, high, duration) + sdl3.SDL_RumbleGamepad(controller, low, high, duration) return max_norm @dataclass class ControllerContext: - controller: sdl2.SDL_GameController + controller: sdl3.SDL_Gamepad instance_id: int controller_index: int stable_id: str @@ -258,11 +258,11 @@ class ControllerContext: last_debug_imu_print: float = 0.0 -def capture_stick_offsets(controller: sdl2.SDL_GameController) -> Dict[int, int]: +def capture_stick_offsets(controller: sdl3.SDL_Gamepad) -> Dict[int, int]: """Sample the current stick axes so they can be treated as the neutral center.""" offsets: Dict[int, int] = {} for axis in STICK_AXES: - offsets[axis] = int(sdl2.SDL_GameControllerGetAxis(controller, axis)) + offsets[axis] = int(sdl3.SDL_GetGamepadAxis(controller, axis)) return offsets @@ -298,11 +298,11 @@ def convert_gyro_to_raw(rad_s: float, scale: float = 1.0) -> int: def initialize_controller_sensors(ctx: ControllerContext, console: Console) -> None: """Enable SDL accelerometer and gyroscope on the controller if available.""" if not all( - hasattr(sdl2, name) + hasattr(sdl3, name) for name in ( - "SDL_GameControllerHasSensor", - "SDL_GameControllerSetSensorEnabled", - "SDL_GameControllerGetSensorData", + "SDL_GamepadHasSensor", + "SDL_SetGamepadSensorEnabled", + "SDL_GetGamepadSensorData", ) ): ctx.sensors_supported = False @@ -311,22 +311,18 @@ def initialize_controller_sensors(ctx: ControllerContext, console: Console) -> N if SENSOR_ACCEL is None or SENSOR_GYRO is None: ctx.sensors_supported = False return - accel_ok = bool(sdl2.SDL_GameControllerHasSensor(ctx.controller, SENSOR_ACCEL)) - gyro_ok = bool(sdl2.SDL_GameControllerHasSensor(ctx.controller, SENSOR_GYRO)) + accel_ok = bool(sdl3.SDL_GamepadHasSensor(ctx.controller, SENSOR_ACCEL)) + gyro_ok = bool(sdl3.SDL_GamepadHasSensor(ctx.controller, SENSOR_GYRO)) ctx.sensors_supported = accel_ok and gyro_ok if not ctx.sensors_supported: console.print( f"[yellow]Controller {ctx.controller_index} has no accelerometer/gyro sensors[/yellow]" ) return - accel_enabled = ( - sdl2.SDL_GameControllerSetSensorEnabled(ctx.controller, SENSOR_ACCEL, SDL_TRUE) - == 0 - ) - gyro_enabled = ( - sdl2.SDL_GameControllerSetSensorEnabled(ctx.controller, SENSOR_GYRO, SDL_TRUE) - == 0 + accel_enabled = sdl3.SDL_SetGamepadSensorEnabled( + ctx.controller, SENSOR_ACCEL, SDL_TRUE ) + gyro_enabled = sdl3.SDL_SetGamepadSensorEnabled(ctx.controller, SENSOR_GYRO, SDL_TRUE) ctx.sensors_enabled = accel_enabled and gyro_enabled if not ctx.sensors_enabled: console.print( @@ -502,7 +498,7 @@ def zero_all_context_sticks( def controller_display_name(ctx: ControllerContext) -> str: """Return a human-readable controller name.""" - name = sdl2.SDL_GameControllerName(ctx.controller) + name = sdl3.SDL_GetGamepadName(ctx.controller) if not name: return "Unknown" if isinstance(name, bytes): @@ -582,15 +578,15 @@ def prompt_swap_abxy_controller( toggle_abxy_for_context(ctx, config, console) -def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int, str]: +def open_controller(instance_id: int) -> Tuple[sdl3.SDL_Gamepad, int, str]: """Open an SDL GameController by index and return it with instance ID and GUID string.""" - controller = sdl2.SDL_GameControllerOpen(index) + controller = sdl3.SDL_OpenGamepad(instance_id) if not controller: raise RuntimeError( - f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}" + f"Failed to open controller {instance_id}: {sdl3.SDL_GetError().decode()}" ) - joystick = sdl2.SDL_GameControllerGetJoystick(controller) - instance_id = sdl2.SDL_JoystickInstanceID(joystick) + joystick = sdl3.SDL_GetGamepadJoystick(controller) + instance_id = sdl3.SDL_GetJoystickID(joystick) guid_str = guid_string_from_joystick(joystick) return controller, instance_id, guid_str @@ -603,19 +599,19 @@ def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: return None -def guid_string_from_joystick(joystick: sdl2.SDL_Joystick) -> str: +def guid_string_from_joystick(joystick: sdl3.SDL_Joystick) -> str: """Return a GUID string for an already-open joystick.""" - guid = sdl2.SDL_JoystickGetGUID(joystick) + guid = sdl3.SDL_GetJoystickGUID(joystick) buf = create_string_buffer(33) - sdl2.SDL_JoystickGetGUIDString(guid, buf, 33) + sdl3.SDL_GUIDToString(guid, buf, 33) return buf.value.decode().lower() if buf.value else "" -def guid_string_for_device_index(index: int) -> str: - """Return a GUID string for a joystick device index without opening it.""" - guid = sdl2.SDL_JoystickGetDeviceGUID(index) +def guid_string_for_instance_id(instance_id: int) -> str: + """Return a GUID string for a joystick instance ID without opening it.""" + guid = sdl3.SDL_GetJoystickGUIDForID(instance_id) buf = create_string_buffer(33) - sdl2.SDL_JoystickGetGUIDString(guid, buf, 33) + sdl3.SDL_GUIDToString(guid, buf, 33) return buf.value.decode().lower() if buf.value else "" @@ -786,7 +782,7 @@ def poll_controller_buttons( """Update button/hat state based on current SDL controller readings.""" changed = False for sdl_button, switch_bit in button_map.items(): - pressed = bool(sdl2.SDL_GameControllerGetButton(ctx.controller, sdl_button)) + pressed = bool(sdl3.SDL_GetGamepadButton(ctx.controller, sdl_button)) previous = ctx.button_state.get(sdl_button) if previous == pressed: continue @@ -799,7 +795,7 @@ def poll_controller_buttons( dpad_changed = False for sdl_button, name in DPAD_BUTTONS.items(): - pressed = bool(sdl2.SDL_GameControllerGetButton(ctx.controller, sdl_button)) + pressed = bool(sdl3.SDL_GetGamepadButton(ctx.controller, sdl_button)) if ctx.dpad[name] == pressed: continue ctx.dpad[name] = pressed @@ -827,6 +823,25 @@ class BridgeConfig: gyro_scale: float = 1.0 +class DisplayIndexAllocator: + """Assigns stable 0-based display indices to controllers, recycling freed slots.""" + + def __init__(self) -> None: + self._next: int = 0 + self._free: List[int] = [] + + def allocate(self) -> int: + if self._free: + return self._free.pop(0) + idx = self._next + self._next += 1 + return idx + + def release(self, idx: int) -> None: + self._free.append(idx) + self._free.sort() + + @dataclass class PairingState: mapping_by_index: Dict[int, str] @@ -838,6 +853,7 @@ class PairingState: ignore_port_desc: List[str] = field(default_factory=list) include_port_desc: List[str] = field(default_factory=list) include_port_mfr: List[str] = field(default_factory=list) + display_index_alloc: DisplayIndexAllocator = field(default_factory=DisplayIndexAllocator) def load_button_maps( @@ -853,16 +869,16 @@ def load_button_maps( mappings_to_load.extend(args.sdl_mapping) button_map_default = dict(BUTTON_MAP) button_map_swapped = dict(BUTTON_MAP) - button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_A] = SwitchButton.B - button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_B] = SwitchButton.A - button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_X] = SwitchButton.Y - button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_Y] = SwitchButton.X + button_map_swapped[sdl3.SDL_GAMEPAD_BUTTON_SOUTH] = SwitchButton.A + button_map_swapped[sdl3.SDL_GAMEPAD_BUTTON_EAST] = SwitchButton.B + button_map_swapped[sdl3.SDL_GAMEPAD_BUTTON_WEST] = SwitchButton.X + button_map_swapped[sdl3.SDL_GAMEPAD_BUTTON_NORTH] = SwitchButton.Y swap_abxy_indices = { idx for idx in args.swap_abxy_index if idx is not None and idx >= 0 } for mapping_path in mappings_to_load: try: - loaded = sdl2.SDL_GameControllerAddMappingsFromFile(mapping_path.encode()) + loaded = sdl3.SDL_AddGamepadMappingsFromFile(mapping_path.encode()) console.print( f"[green]Loaded {loaded} SDL mapping(s) from {mapping_path}[/green]" ) @@ -902,70 +918,76 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon def initialize_sdl(parser: argparse.ArgumentParser) -> None: """Set SDL hints and initialize subsystems needed for controllers.""" - sdl2.SDL_SetHint(sdl2.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, b"1") + sdl3.SDL_SetHint(sdl3.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, b"1") set_hint("SDL_JOYSTICK_HIDAPI", "1") set_hint("SDL_JOYSTICK_HIDAPI_SWITCH", "1") - # Use controller button labels so Nintendo layouts (ABXY) map correctly on Linux. - set_hint("SDL_GAMECONTROLLER_USE_BUTTON_LABELS", "1") - if ( - sdl2.SDL_Init( - sdl2.SDL_INIT_GAMECONTROLLER - | sdl2.SDL_INIT_JOYSTICK - | sdl2.SDL_INIT_EVERYTHING - ) - != 0 - ): - parser.error(f"SDL init failed: {sdl2.SDL_GetError().decode(errors='ignore')}") + if not sdl3.SDL_Init(sdl3.SDL_INIT_GAMEPAD | sdl3.SDL_INIT_JOYSTICK): + parser.error(f"SDL init failed: {sdl3.SDL_GetError().decode(errors='ignore')}") def detect_controllers( console: Console, args: argparse.Namespace, parser: argparse.ArgumentParser ) -> Tuple[List[int], Dict[int, str]]: """Detect available controllers and return usable indices and names.""" - controller_indices: List[int] = [] + controller_ids: List[int] = [] controller_names: Dict[int, str] = {} - controller_count = sdl2.SDL_NumJoysticks() - if controller_count < 0: - parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") + count = ctypes.c_int(0) + joystick_ids = sdl3.SDL_GetJoysticks(ctypes.byref(count)) + if not joystick_ids: + if count.value < 0: + parser.error(f"SDL error: {sdl3.SDL_GetError().decode()}") + return controller_ids, controller_names include_controller_name = [n.lower() for n in args.include_controller_name] - for index in range(controller_count): - if sdl2.SDL_IsGameController(index): - name = sdl2.SDL_GameControllerNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) + display_counter = 0 + for i in range(count.value): + instance_id = joystick_ids[i] + if sdl3.SDL_IsGamepad(instance_id): + name = sdl3.SDL_GetGamepadNameForID(instance_id) + name_str = ( + name.decode() if isinstance(name, bytes) else str(name) if name else "Unknown" + ) if include_controller_name and all( substr not in name_str.lower() for substr in include_controller_name ): console.print( - f"[yellow]Skipping controller {index} ({name_str}) due to name filter[/yellow]" + f"[yellow]Skipping controller ({name_str}) due to name filter[/yellow]" ) continue - console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") - controller_indices.append(index) - controller_names[index] = name_str + console.print(f"[cyan]Detected controller {display_counter}: ({name_str})[/cyan]") + display_counter += 1 + controller_ids.append(instance_id) + controller_names[instance_id] = name_str else: - name = sdl2.SDL_JoystickNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) + name = sdl3.SDL_GetJoystickNameForID(instance_id) + name_str = ( + name.decode() if isinstance(name, bytes) else str(name) if name else "Unknown" + ) if include_controller_name and all( substr not in name_str.lower() for substr in include_controller_name ): console.print( - f"[yellow]Skipping joystick {index} ({name_str}) due to name filter[/yellow]" + f"[yellow]Skipping joystick {instance_id} ({name_str}) due to name filter[/yellow]" ) continue console.print( - f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]" + f"[yellow]Found joystick {instance_id} (not a GameController): {name_str}[/yellow]" ) - return controller_indices, controller_names + sdl3.SDL_free(joystick_ids) + return controller_ids, controller_names def list_controllers_with_guids( console: Console, parser: argparse.ArgumentParser ) -> None: """Print detected controllers with their GUID strings and exit.""" - count = sdl2.SDL_NumJoysticks() - if count < 0: - parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") - if count == 0: + count = ctypes.c_int(0) + joystick_ids = sdl3.SDL_GetJoysticks(ctypes.byref(count)) + if not joystick_ids: + if count.value < 0: + parser.error(f"SDL error: {sdl3.SDL_GetError().decode()}") + console.print("[yellow]No controllers detected.[/yellow]") + return + if count.value == 0: console.print("[yellow]No controllers detected.[/yellow]") return table = Table(title="Detected Controllers (GUIDs)") @@ -973,18 +995,20 @@ def list_controllers_with_guids( table.add_column("Type") table.add_column("Name") table.add_column("GUID") - for idx in range(count): - is_gc = sdl2.SDL_IsGameController(idx) + for i in range(count.value): + instance_id = joystick_ids[i] + is_gc = sdl3.SDL_IsGamepad(instance_id) name = ( - sdl2.SDL_GameControllerNameForIndex(idx) + sdl3.SDL_GetGamepadNameForID(instance_id) if is_gc - else sdl2.SDL_JoystickNameForIndex(idx) + else sdl3.SDL_GetJoystickNameForID(instance_id) ) - name_str = name.decode() if isinstance(name, bytes) else str(name) - guid_str = guid_string_for_device_index(idx) + name_str = name.decode() if isinstance(name, bytes) else str(name) if name else "Unknown" + guid_str = guid_string_for_instance_id(instance_id) table.add_row( - str(idx), "GameController" if is_gc else "Joystick", name_str, guid_str + str(instance_id), "GameController" if is_gc else "Joystick", name_str, guid_str ) + sdl3.SDL_free(joystick_ids) console.print(table) @@ -1074,7 +1098,7 @@ def assign_port_for_index( return port_choice -def ports_in_use(pairing: PairingState, contexts: Dict[int, ControllerContext]) -> set: +def ports_in_use(pairing: PairingState, contexts: Dict[int, ControllerContext]) -> set[str]: """Return a set of UART paths currently reserved or mapped.""" used = set(pairing.mapping_by_index.values()) used.update(ctx.port for ctx in contexts.values() if ctx.port) @@ -1107,7 +1131,7 @@ def handle_removed_port( ctx.uart.close() except Exception: pass - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + sdl3.SDL_RumbleGamepad(ctx.controller, 0, 0, 0) ctx.uart = None ctx.port = None ctx.rumble_active = False @@ -1190,44 +1214,47 @@ def open_initial_contexts( """Open initial controllers and UARTs for detected indices.""" contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] - for index in controller_indices: - if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): - name = sdl2.SDL_JoystickNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) + for instance_id in controller_indices: + if not sdl3.SDL_IsGamepad(instance_id): + name = sdl3.SDL_GetJoystickNameForID(instance_id) + name_str = name.decode() if isinstance(name, bytes) else str(name) if name else "Unknown" console.print( - f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" + f"[yellow]ID {instance_id} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" ) continue - port = assign_port_for_index(pairing, index, console) + display_idx = pairing.display_index_alloc.allocate() + port = assign_port_for_index(pairing, display_idx, console) if port is None and not pairing.auto_pairing_enabled: + pairing.display_index_alloc.release(display_idx) continue try: - controller, instance_id, guid = open_controller(index) + controller, opened_instance_id, guid = open_controller(instance_id) except Exception as exc: - console.print(f"[red]Failed to open controller {index}: {exc}[/red]") + console.print(f"[red]Failed to open controller {display_idx}: {exc}[/red]") + pairing.display_index_alloc.release(display_idx) continue stable_id = guid should_swap = ( - index in config.swap_abxy_indices or stable_id in config.swap_abxy_ids + display_idx in config.swap_abxy_indices or stable_id in config.swap_abxy_ids ) uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) console.print( - f"[green]Controller {index} (id {stable_id}, inst {instance_id}) paired to {port}[/green]" + f"[green]Controller {display_idx} (id {stable_id}, inst {opened_instance_id}) paired to {port}[/green]" ) elif port: console.print( - f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]" + f"[yellow]Controller {display_idx} (id {stable_id}, inst {opened_instance_id}) waiting for UART {port}[/yellow]" ) else: console.print( - f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" + f"[yellow]Controller {display_idx} (id {stable_id}, inst {opened_instance_id}) connected; waiting for an available UART[/yellow]" ) ctx = ControllerContext( controller=controller, - instance_id=instance_id, - controller_index=index, + instance_id=opened_instance_id, + controller_index=display_idx, stable_id=stable_id, port=port, uart=uart, @@ -1237,28 +1264,28 @@ def open_initial_contexts( initialize_controller_sensors(ctx, console) if config.zero_sticks: zero_context_sticks(ctx, console) - contexts[instance_id] = ctx + contexts[opened_instance_id] = ctx return contexts, uarts def handle_axis_motion( - event: sdl2.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig + event: sdl3.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig ) -> None: """Process axis motion event into stick/trigger state.""" - ctx = contexts.get(event.caxis.which) + ctx = contexts.get(event.gaxis.which) if not ctx: return - axis = event.caxis.axis - value = calibrate_axis_value(event.caxis.value, axis, ctx) - if axis == sdl2.SDL_CONTROLLER_AXIS_LEFTX: + axis = event.gaxis.axis + value = calibrate_axis_value(event.gaxis.value, axis, ctx) + if axis == sdl3.SDL_GAMEPAD_AXIS_LEFTX: ctx.report.lx = axis_to_stick(value, config.deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_LEFTY: + elif axis == sdl3.SDL_GAMEPAD_AXIS_LEFTY: ctx.report.ly = axis_to_stick(value, config.deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTX: + elif axis == sdl3.SDL_GAMEPAD_AXIS_RIGHTX: ctx.report.rx = axis_to_stick(value, config.deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTY: + elif axis == sdl3.SDL_GAMEPAD_AXIS_RIGHTY: ctx.report.ry = axis_to_stick(value, config.deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERLEFT: + elif axis == sdl3.SDL_GAMEPAD_AXIS_LEFT_TRIGGER: pressed = trigger_to_button(value, config.trigger_threshold) if pressed != ctx.last_trigger_state["left"]: if pressed: @@ -1266,7 +1293,7 @@ def handle_axis_motion( else: ctx.report.buttons &= ~SwitchButton.ZL ctx.last_trigger_state["left"] = pressed - elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + elif axis == sdl3.SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: pressed = trigger_to_button(value, config.trigger_threshold) if pressed != ctx.last_trigger_state["right"]: if pressed: @@ -1277,17 +1304,17 @@ def handle_axis_motion( def handle_sensor_update( - event: sdl2.SDL_Event, + event: sdl3.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig, ) -> None: """Process SDL sensor update event into IMU samples.""" - ctx = contexts.get(event.csensor.which) + ctx = contexts.get(event.gsensor.which) if ctx is None or not ctx.sensors_enabled or config.no_imu: return - sensor_type = event.csensor.sensor - data = event.csensor.data + sensor_type = event.gsensor.sensor + data = event.gsensor.data if sensor_type == SENSOR_ACCEL: ctx.last_accel = (float(data[0]), float(data[1]), float(data[2])) @@ -1359,12 +1386,12 @@ def handle_sensor_update( def handle_button_event( - event: sdl2.SDL_Event, + event: sdl3.SDL_Event, config: BridgeConfig, contexts: Dict[int, ControllerContext], ) -> None: """Process button events into report/dpad state.""" - ctx = contexts.get(event.cbutton.which) + ctx = contexts.get(event.gbutton.which) if not ctx: return current_button_map = ( @@ -1372,8 +1399,8 @@ def handle_button_event( if (config.swap_abxy_global or ctx.swap_abxy) else config.button_map_default ) - button = event.cbutton.button - pressed = event.type == sdl2.SDL_CONTROLLERBUTTONDOWN + button = event.gbutton.button + pressed = event.type == sdl3.SDL_EVENT_GAMEPAD_BUTTON_DOWN if button in current_button_map: bit = current_button_map[button] if pressed: @@ -1387,7 +1414,7 @@ def handle_button_event( def handle_device_added( - event: sdl2.SDL_Event, + event: sdl3.SDL_Event, args: argparse.Namespace, pairing: PairingState, contexts: Dict[int, ControllerContext], @@ -1396,46 +1423,47 @@ def handle_device_added( config: BridgeConfig, ) -> None: """Handle controller hotplug by opening and pairing UART if possible.""" - idx = event.cdevice.which - # If we already have a context for this logical index, ignore the duplicate event. - if any(c.controller_index == idx for c in contexts.values()): + sdl_id = event.gdevice.which + if sdl_id in contexts: return - port = assign_port_for_index(pairing, idx, console) - if port is None and not pairing.auto_pairing_enabled: - return - if idx >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(idx): - name = sdl2.SDL_JoystickNameForIndex(idx) - name_str = name.decode() if isinstance(name, bytes) else str(name) + if not sdl3.SDL_IsGamepad(sdl_id): + name = sdl3.SDL_GetJoystickNameForID(sdl_id) + name_str = name.decode() if isinstance(name, bytes) else str(name) if name else "Unknown" console.print( - f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" + f"[yellow]Device {sdl_id} is not a GameController ({name_str}).[/yellow]" ) return + display_idx = pairing.display_index_alloc.allocate() + port = assign_port_for_index(pairing, display_idx, console) + if port is None and not pairing.auto_pairing_enabled: + pairing.display_index_alloc.release(display_idx) + return try: - controller, instance_id, guid = open_controller(idx) + controller, instance_id, guid = open_controller(sdl_id) except Exception as exc: - console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") + console.print(f"[red]Hotplug open failed for controller {display_idx}: {exc}[/red]") + pairing.display_index_alloc.release(display_idx) return stable_id = guid - # Determine if this controller should have swapped ABXY based on index or GUID - should_swap = idx in config.swap_abxy_indices or stable_id in config.swap_abxy_ids + should_swap = display_idx in config.swap_abxy_indices or stable_id in config.swap_abxy_ids uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) console.print( - f"[green]Controller {idx} (id {stable_id}, inst {instance_id}) paired to {port}[/green]" + f"[green]Controller {display_idx} (id {stable_id}, inst {instance_id}) paired to {port}[/green]" ) elif port: console.print( - f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]" + f"[yellow]Controller {display_idx} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]" ) else: console.print( - f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" + f"[yellow]Controller {display_idx} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" ) ctx = ControllerContext( controller=controller, instance_id=instance_id, - controller_index=idx, + controller_index=display_idx, stable_id=stable_id, port=port, uart=uart, @@ -1449,19 +1477,31 @@ def handle_device_added( def handle_device_removed( - event: sdl2.SDL_Event, + event: sdl3.SDL_Event, pairing: PairingState, contexts: Dict[int, ControllerContext], + uarts: List[PicoUART], console: Console, ) -> None: """Handle controller removal and release any auto-assigned UART.""" - instance_id = event.cdevice.which + instance_id = event.gdevice.which ctx = contexts.pop(instance_id, None) if not ctx: return console.print( - f"[yellow]Controller {instance_id} (id {ctx.stable_id}) removed[/yellow]" + f"[yellow]Controller {ctx.controller_index} (id {ctx.stable_id}) removed[/yellow]" ) + # Close the UART handle *before* returning the port to the pool so the + # next consumer can actually open it (Windows holds the port exclusively). + if ctx.uart: + try: + ctx.uart.close() + except Exception: + pass + if ctx.uart in uarts: + uarts.remove(ctx.uart) + ctx.uart = None + sdl3.SDL_RumbleGamepad(ctx.controller, 0, 0, 0) if ctx.controller_index in pairing.auto_assigned_indices: # Return auto-paired UART back to the pool so a future device can use it. freed = pairing.mapping_by_index.pop(ctx.controller_index, None) @@ -1469,7 +1509,8 @@ def handle_device_removed( if freed and freed not in pairing.available_ports: pairing.available_ports.append(freed) console.print(f"[cyan]Released UART {freed} back to pool[/cyan]") - sdl2.SDL_GameControllerClose(ctx.controller) + pairing.display_index_alloc.release(ctx.controller_index) + sdl3.SDL_CloseGamepad(ctx.controller) def service_contexts( @@ -1530,14 +1571,14 @@ def service_contexts( ctx.last_rumble_energy = energy ctx.last_rumble = now elif ctx.rumble_active and (now - ctx.last_rumble) > RUMBLE_IDLE_TIMEOUT: - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + sdl3.SDL_RumbleGamepad(ctx.controller, 0, 0, 0) ctx.rumble_active = False ctx.last_rumble_energy = 0.0 elif ( ctx.rumble_active and (now - ctx.last_rumble_change) > RUMBLE_STUCK_TIMEOUT ): - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + sdl3.SDL_RumbleGamepad(ctx.controller, 0, 0, 0) ctx.rumble_active = False ctx.last_rumble_energy = 0.0 except SerialException as exc: @@ -1546,7 +1587,7 @@ def service_contexts( ctx.uart.close() except Exception: pass - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + sdl3.SDL_RumbleGamepad(ctx.controller, 0, 0, 0) ctx.uart = None ctx.rumble_active = False ctx.last_rumble_energy = 0.0 @@ -1565,31 +1606,31 @@ def run_bridge_loop( hotkey: Optional[HotkeyMonitor] = None, ) -> None: """Main event loop for bridging controllers to UART and handling rumble.""" - event = sdl2.SDL_Event() + event = sdl3.SDL_Event() port_scan_interval = 2.0 last_port_scan = time.monotonic() running = True while running: - while sdl2.SDL_PollEvent(event): - if event.type == sdl2.SDL_QUIT: + while sdl3.SDL_PollEvent(ctypes.byref(event)): + if event.type == sdl3.SDL_EVENT_QUIT: running = False break - if event.type == sdl2.SDL_CONTROLLERAXISMOTION: + if event.type == sdl3.SDL_EVENT_GAMEPAD_AXIS_MOTION: handle_axis_motion(event, contexts, config) elif event.type in ( - sdl2.SDL_CONTROLLERBUTTONDOWN, - sdl2.SDL_CONTROLLERBUTTONUP, + sdl3.SDL_EVENT_GAMEPAD_BUTTON_DOWN, + sdl3.SDL_EVENT_GAMEPAD_BUTTON_UP, ): handle_button_event(event, config, contexts) - elif event.type == SDL_CONTROLLERSENSORUPDATE: + elif event.type == SDL_EVENT_GAMEPAD_SENSOR_UPDATE: handle_sensor_update(event, contexts, config) - elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: + elif event.type == sdl3.SDL_EVENT_GAMEPAD_ADDED: handle_device_added( event, args, pairing, contexts, uarts, console, config ) - elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: - handle_device_removed(event, pairing, contexts, console) + elif event.type == sdl3.SDL_EVENT_GAMEPAD_REMOVED: + handle_device_removed(event, pairing, contexts, uarts, console) now = time.monotonic() if now - last_port_scan > port_scan_interval: @@ -1606,16 +1647,16 @@ def run_bridge_loop( zero_all_context_sticks(contexts, console) elif key == config.swap_hotkey: prompt_swap_abxy_controller(contexts, config, console, hotkey) - sdl2.SDL_Delay(1) + sdl3.SDL_Delay(1) def cleanup(contexts: Dict[int, ControllerContext], uarts: List[PicoUART]) -> None: """Gracefully close controllers, UARTs, and SDL subsystems.""" for ctx in contexts.values(): - sdl2.SDL_GameControllerClose(ctx.controller) + sdl3.SDL_CloseGamepad(ctx.controller) for uart in uarts: uart.close() - sdl2.SDL_Quit() + sdl3.SDL_Quit() def main() -> None: diff --git a/src/switch_pico_bridge/switch_pico_uart.py b/src/switch_pico_bridge/switch_pico_uart.py index 2fdab53..81b2f54 100644 --- a/src/switch_pico_bridge/switch_pico_uart.py +++ b/src/switch_pico_bridge/switch_pico_uart.py @@ -36,10 +36,10 @@ ACCEL_LSB_PER_G = 4096.0 GYRO_LSB_PER_RAD_S = 818.5 try: - import sdl2 as _sdl2 # type: ignore[import-not-found] + import sdl3 as _sdl3 # type: ignore[import-not-found] - _sensor_accel = getattr(_sdl2, "SDL_SENSOR_ACCEL", 1) - _sensor_gyro = getattr(_sdl2, "SDL_SENSOR_GYRO", 2) + _sensor_accel = getattr(_sdl3, "SDL_SENSOR_ACCEL", 1) + _sensor_gyro = getattr(_sdl3, "SDL_SENSOR_GYRO", 2) except ImportError: _sensor_accel = 1 _sensor_gyro = 2 From 311bec8f0d6a8b8b32120c88b844d8b7c9925611 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 16:54:58 -0600 Subject: [PATCH 118/134] feat(bridge): migrate to SDL3 and fix controller hotplug issues - Migrate all SDL2 GameController API calls to SDL3 Gamepad equivalents - Fix PermissionError on controller reconnect by closing UART handle before releasing port back to pool (Windows holds COM ports exclusively) - Add DisplayIndexAllocator so controllers get stable display indices (0, 0, 0) instead of incrementing SDL instance IDs (1, 2, 3) on each reconnect - Button mapping now uses SDL3 positional constants (SOUTH/EAST/WEST/NORTH) which works correctly for all controller types without USE_BUTTON_LABELS hint Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- .../controller_uart_bridge.py | 399 ++++++++++-------- src/switch_pico_bridge/switch_pico_uart.py | 6 +- 2 files changed, 223 insertions(+), 182 deletions(-) diff --git a/src/switch_pico_bridge/controller_uart_bridge.py b/src/switch_pico_bridge/controller_uart_bridge.py index 7b280d3..a5a22af 100644 --- a/src/switch_pico_bridge/controller_uart_bridge.py +++ b/src/switch_pico_bridge/controller_uart_bridge.py @@ -16,6 +16,7 @@ Features inspired by ``host/controller_bridge.py``: from __future__ import annotations import argparse +import ctypes import os import sys import time @@ -26,8 +27,7 @@ from pathlib import Path from typing import Dict, List, Optional, Tuple from serial import SerialException -import sdl2 -import sdl2.ext +import sdl3 from rich.console import Console from rich.prompt import Prompt from rich.table import Table @@ -58,8 +58,8 @@ RUMBLE_STUCK_TIMEOUT = 0.60 # continuous same-energy rumble will be stopped aft RUMBLE_MIN_ACTIVE = 0.40 # below this, rumble is treated as off/noise RUMBLE_SCALE = 1.0 CONTROLLER_DB_URL_DEFAULT = "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt" -SDL_TRUE = getattr(sdl2, "SDL_TRUE", 1) -SDL_CONTROLLERSENSORUPDATE = getattr(sdl2, "SDL_CONTROLLERSENSORUPDATE", 0x658) +SDL_TRUE = True +SDL_EVENT_GAMEPAD_SENSOR_UPDATE = getattr(sdl3, "SDL_EVENT_GAMEPAD_SENSOR_UPDATE", 0x658) GYRO_BIAS_SAMPLES = 200 IMU_BUFFER_SIZE = 32 @@ -121,41 +121,41 @@ def parse_hotkey(value: str) -> str: def set_hint(name: str, value: str) -> None: - """Set an SDL hint safely even if the constant is missing in PySDL2.""" + """Set an SDL hint safely even if the constant is missing.""" try: - sdl2.SDL_SetHint(name.encode(), value.encode()) + sdl3.SDL_SetHint(name.encode(), value.encode()) except Exception: pass BUTTON_MAP = { # Direct mapping to the Switch PRO mask definitions in switch_pro_descriptors.h - sdl2.SDL_CONTROLLER_BUTTON_A: SwitchButton.A, - sdl2.SDL_CONTROLLER_BUTTON_B: SwitchButton.B, - sdl2.SDL_CONTROLLER_BUTTON_X: SwitchButton.X, - sdl2.SDL_CONTROLLER_BUTTON_Y: SwitchButton.Y, - sdl2.SDL_CONTROLLER_BUTTON_LEFTSHOULDER: SwitchButton.L, - sdl2.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: SwitchButton.R, - sdl2.SDL_CONTROLLER_BUTTON_BACK: SwitchButton.MINUS, - sdl2.SDL_CONTROLLER_BUTTON_START: SwitchButton.PLUS, - sdl2.SDL_CONTROLLER_BUTTON_GUIDE: SwitchButton.HOME, - sdl2.SDL_CONTROLLER_BUTTON_MISC1: SwitchButton.CAPTURE, - sdl2.SDL_CONTROLLER_BUTTON_LEFTSTICK: SwitchButton.LCLICK, - sdl2.SDL_CONTROLLER_BUTTON_RIGHTSTICK: SwitchButton.RCLICK, + sdl3.SDL_GAMEPAD_BUTTON_SOUTH: SwitchButton.B, + sdl3.SDL_GAMEPAD_BUTTON_EAST: SwitchButton.A, + sdl3.SDL_GAMEPAD_BUTTON_WEST: SwitchButton.Y, + sdl3.SDL_GAMEPAD_BUTTON_NORTH: SwitchButton.X, + sdl3.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: SwitchButton.L, + sdl3.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: SwitchButton.R, + sdl3.SDL_GAMEPAD_BUTTON_BACK: SwitchButton.MINUS, + sdl3.SDL_GAMEPAD_BUTTON_START: SwitchButton.PLUS, + sdl3.SDL_GAMEPAD_BUTTON_GUIDE: SwitchButton.HOME, + sdl3.SDL_GAMEPAD_BUTTON_MISC1: SwitchButton.CAPTURE, + sdl3.SDL_GAMEPAD_BUTTON_LEFT_STICK: SwitchButton.LCLICK, + sdl3.SDL_GAMEPAD_BUTTON_RIGHT_STICK: SwitchButton.RCLICK, } DPAD_BUTTONS = { - sdl2.SDL_CONTROLLER_BUTTON_DPAD_UP: "up", - sdl2.SDL_CONTROLLER_BUTTON_DPAD_DOWN: "down", - sdl2.SDL_CONTROLLER_BUTTON_DPAD_LEFT: "left", - sdl2.SDL_CONTROLLER_BUTTON_DPAD_RIGHT: "right", + sdl3.SDL_GAMEPAD_BUTTON_DPAD_UP: "up", + sdl3.SDL_GAMEPAD_BUTTON_DPAD_DOWN: "down", + sdl3.SDL_GAMEPAD_BUTTON_DPAD_LEFT: "left", + sdl3.SDL_GAMEPAD_BUTTON_DPAD_RIGHT: "right", } STICK_AXIS_LABELS = ( - (sdl2.SDL_CONTROLLER_AXIS_LEFTX, "LX"), - (sdl2.SDL_CONTROLLER_AXIS_LEFTY, "LY"), - (sdl2.SDL_CONTROLLER_AXIS_RIGHTX, "RX"), - (sdl2.SDL_CONTROLLER_AXIS_RIGHTY, "RY"), + (sdl3.SDL_GAMEPAD_AXIS_LEFTX, "LX"), + (sdl3.SDL_GAMEPAD_AXIS_LEFTY, "LY"), + (sdl3.SDL_GAMEPAD_AXIS_RIGHTX, "RX"), + (sdl3.SDL_GAMEPAD_AXIS_RIGHTY, "RY"), ) STICK_AXES = tuple(axis for axis, _ in STICK_AXIS_LABELS) @@ -200,26 +200,26 @@ def interactive_pairing( return mappings -def apply_rumble(controller: sdl2.SDL_GameController, payload: bytes) -> float: +def apply_rumble(controller: sdl3.SDL_Gamepad, payload: bytes) -> float: """Apply rumble payload to SDL controller and return max normalized energy.""" left_norm, right_norm = decode_rumble(payload) max_norm = max(left_norm, right_norm) # Treat small rumble as "off" to avoid idle buzz. if max_norm < RUMBLE_MIN_ACTIVE: - sdl2.SDL_GameControllerRumble(controller, 0, 0, 0) + sdl3.SDL_RumbleGamepad(controller, 0, 0, 0) return 0.0 # Attenuate to feel closer to a real controller; cap at ~25% strength. scale = RUMBLE_SCALE low = int(min(1.0, left_norm * scale) * 0xFFFF) # SDL: low_frequency_rumble high = int(min(1.0, right_norm * scale) * 0xFFFF) # SDL: high_frequency_rumble duration = 10 - sdl2.SDL_GameControllerRumble(controller, low, high, duration) + sdl3.SDL_RumbleGamepad(controller, low, high, duration) return max_norm @dataclass class ControllerContext: - controller: sdl2.SDL_GameController + controller: sdl3.SDL_Gamepad instance_id: int controller_index: int stable_id: str @@ -258,11 +258,11 @@ class ControllerContext: last_debug_imu_print: float = 0.0 -def capture_stick_offsets(controller: sdl2.SDL_GameController) -> Dict[int, int]: +def capture_stick_offsets(controller: sdl3.SDL_Gamepad) -> Dict[int, int]: """Sample the current stick axes so they can be treated as the neutral center.""" offsets: Dict[int, int] = {} for axis in STICK_AXES: - offsets[axis] = int(sdl2.SDL_GameControllerGetAxis(controller, axis)) + offsets[axis] = int(sdl3.SDL_GetGamepadAxis(controller, axis)) return offsets @@ -298,11 +298,11 @@ def convert_gyro_to_raw(rad_s: float, scale: float = 1.0) -> int: def initialize_controller_sensors(ctx: ControllerContext, console: Console) -> None: """Enable SDL accelerometer and gyroscope on the controller if available.""" if not all( - hasattr(sdl2, name) + hasattr(sdl3, name) for name in ( - "SDL_GameControllerHasSensor", - "SDL_GameControllerSetSensorEnabled", - "SDL_GameControllerGetSensorData", + "SDL_GamepadHasSensor", + "SDL_SetGamepadSensorEnabled", + "SDL_GetGamepadSensorData", ) ): ctx.sensors_supported = False @@ -311,22 +311,18 @@ def initialize_controller_sensors(ctx: ControllerContext, console: Console) -> N if SENSOR_ACCEL is None or SENSOR_GYRO is None: ctx.sensors_supported = False return - accel_ok = bool(sdl2.SDL_GameControllerHasSensor(ctx.controller, SENSOR_ACCEL)) - gyro_ok = bool(sdl2.SDL_GameControllerHasSensor(ctx.controller, SENSOR_GYRO)) + accel_ok = bool(sdl3.SDL_GamepadHasSensor(ctx.controller, SENSOR_ACCEL)) + gyro_ok = bool(sdl3.SDL_GamepadHasSensor(ctx.controller, SENSOR_GYRO)) ctx.sensors_supported = accel_ok and gyro_ok if not ctx.sensors_supported: console.print( f"[yellow]Controller {ctx.controller_index} has no accelerometer/gyro sensors[/yellow]" ) return - accel_enabled = ( - sdl2.SDL_GameControllerSetSensorEnabled(ctx.controller, SENSOR_ACCEL, SDL_TRUE) - == 0 - ) - gyro_enabled = ( - sdl2.SDL_GameControllerSetSensorEnabled(ctx.controller, SENSOR_GYRO, SDL_TRUE) - == 0 + accel_enabled = sdl3.SDL_SetGamepadSensorEnabled( + ctx.controller, SENSOR_ACCEL, SDL_TRUE ) + gyro_enabled = sdl3.SDL_SetGamepadSensorEnabled(ctx.controller, SENSOR_GYRO, SDL_TRUE) ctx.sensors_enabled = accel_enabled and gyro_enabled if not ctx.sensors_enabled: console.print( @@ -502,7 +498,7 @@ def zero_all_context_sticks( def controller_display_name(ctx: ControllerContext) -> str: """Return a human-readable controller name.""" - name = sdl2.SDL_GameControllerName(ctx.controller) + name = sdl3.SDL_GetGamepadName(ctx.controller) if not name: return "Unknown" if isinstance(name, bytes): @@ -582,15 +578,15 @@ def prompt_swap_abxy_controller( toggle_abxy_for_context(ctx, config, console) -def open_controller(index: int) -> Tuple[sdl2.SDL_GameController, int, str]: +def open_controller(instance_id: int) -> Tuple[sdl3.SDL_Gamepad, int, str]: """Open an SDL GameController by index and return it with instance ID and GUID string.""" - controller = sdl2.SDL_GameControllerOpen(index) + controller = sdl3.SDL_OpenGamepad(instance_id) if not controller: raise RuntimeError( - f"Failed to open controller {index}: {sdl2.SDL_GetError().decode()}" + f"Failed to open controller {instance_id}: {sdl3.SDL_GetError().decode()}" ) - joystick = sdl2.SDL_GameControllerGetJoystick(controller) - instance_id = sdl2.SDL_JoystickInstanceID(joystick) + joystick = sdl3.SDL_GetGamepadJoystick(controller) + instance_id = sdl3.SDL_GetJoystickID(joystick) guid_str = guid_string_from_joystick(joystick) return controller, instance_id, guid_str @@ -603,19 +599,19 @@ def try_open_uart(port: str, baud: int) -> Optional[PicoUART]: return None -def guid_string_from_joystick(joystick: sdl2.SDL_Joystick) -> str: +def guid_string_from_joystick(joystick: sdl3.SDL_Joystick) -> str: """Return a GUID string for an already-open joystick.""" - guid = sdl2.SDL_JoystickGetGUID(joystick) + guid = sdl3.SDL_GetJoystickGUID(joystick) buf = create_string_buffer(33) - sdl2.SDL_JoystickGetGUIDString(guid, buf, 33) + sdl3.SDL_GUIDToString(guid, buf, 33) return buf.value.decode().lower() if buf.value else "" -def guid_string_for_device_index(index: int) -> str: - """Return a GUID string for a joystick device index without opening it.""" - guid = sdl2.SDL_JoystickGetDeviceGUID(index) +def guid_string_for_instance_id(instance_id: int) -> str: + """Return a GUID string for a joystick instance ID without opening it.""" + guid = sdl3.SDL_GetJoystickGUIDForID(instance_id) buf = create_string_buffer(33) - sdl2.SDL_JoystickGetGUIDString(guid, buf, 33) + sdl3.SDL_GUIDToString(guid, buf, 33) return buf.value.decode().lower() if buf.value else "" @@ -786,7 +782,7 @@ def poll_controller_buttons( """Update button/hat state based on current SDL controller readings.""" changed = False for sdl_button, switch_bit in button_map.items(): - pressed = bool(sdl2.SDL_GameControllerGetButton(ctx.controller, sdl_button)) + pressed = bool(sdl3.SDL_GetGamepadButton(ctx.controller, sdl_button)) previous = ctx.button_state.get(sdl_button) if previous == pressed: continue @@ -799,7 +795,7 @@ def poll_controller_buttons( dpad_changed = False for sdl_button, name in DPAD_BUTTONS.items(): - pressed = bool(sdl2.SDL_GameControllerGetButton(ctx.controller, sdl_button)) + pressed = bool(sdl3.SDL_GetGamepadButton(ctx.controller, sdl_button)) if ctx.dpad[name] == pressed: continue ctx.dpad[name] = pressed @@ -827,6 +823,25 @@ class BridgeConfig: gyro_scale: float = 1.0 +class DisplayIndexAllocator: + """Assigns stable 0-based display indices to controllers, recycling freed slots.""" + + def __init__(self) -> None: + self._next: int = 0 + self._free: List[int] = [] + + def allocate(self) -> int: + if self._free: + return self._free.pop(0) + idx = self._next + self._next += 1 + return idx + + def release(self, idx: int) -> None: + self._free.append(idx) + self._free.sort() + + @dataclass class PairingState: mapping_by_index: Dict[int, str] @@ -838,6 +853,7 @@ class PairingState: ignore_port_desc: List[str] = field(default_factory=list) include_port_desc: List[str] = field(default_factory=list) include_port_mfr: List[str] = field(default_factory=list) + display_index_alloc: DisplayIndexAllocator = field(default_factory=DisplayIndexAllocator) def load_button_maps( @@ -853,16 +869,16 @@ def load_button_maps( mappings_to_load.extend(args.sdl_mapping) button_map_default = dict(BUTTON_MAP) button_map_swapped = dict(BUTTON_MAP) - button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_A] = SwitchButton.B - button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_B] = SwitchButton.A - button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_X] = SwitchButton.Y - button_map_swapped[sdl2.SDL_CONTROLLER_BUTTON_Y] = SwitchButton.X + button_map_swapped[sdl3.SDL_GAMEPAD_BUTTON_SOUTH] = SwitchButton.A + button_map_swapped[sdl3.SDL_GAMEPAD_BUTTON_EAST] = SwitchButton.B + button_map_swapped[sdl3.SDL_GAMEPAD_BUTTON_WEST] = SwitchButton.X + button_map_swapped[sdl3.SDL_GAMEPAD_BUTTON_NORTH] = SwitchButton.Y swap_abxy_indices = { idx for idx in args.swap_abxy_index if idx is not None and idx >= 0 } for mapping_path in mappings_to_load: try: - loaded = sdl2.SDL_GameControllerAddMappingsFromFile(mapping_path.encode()) + loaded = sdl3.SDL_AddGamepadMappingsFromFile(mapping_path.encode()) console.print( f"[green]Loaded {loaded} SDL mapping(s) from {mapping_path}[/green]" ) @@ -902,70 +918,76 @@ def build_bridge_config(console: Console, args: argparse.Namespace) -> BridgeCon def initialize_sdl(parser: argparse.ArgumentParser) -> None: """Set SDL hints and initialize subsystems needed for controllers.""" - sdl2.SDL_SetHint(sdl2.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, b"1") + sdl3.SDL_SetHint(sdl3.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, b"1") set_hint("SDL_JOYSTICK_HIDAPI", "1") set_hint("SDL_JOYSTICK_HIDAPI_SWITCH", "1") - # Use controller button labels so Nintendo layouts (ABXY) map correctly on Linux. - set_hint("SDL_GAMECONTROLLER_USE_BUTTON_LABELS", "1") - if ( - sdl2.SDL_Init( - sdl2.SDL_INIT_GAMECONTROLLER - | sdl2.SDL_INIT_JOYSTICK - | sdl2.SDL_INIT_EVERYTHING - ) - != 0 - ): - parser.error(f"SDL init failed: {sdl2.SDL_GetError().decode(errors='ignore')}") + if not sdl3.SDL_Init(sdl3.SDL_INIT_GAMEPAD | sdl3.SDL_INIT_JOYSTICK): + parser.error(f"SDL init failed: {sdl3.SDL_GetError().decode(errors='ignore')}") def detect_controllers( console: Console, args: argparse.Namespace, parser: argparse.ArgumentParser ) -> Tuple[List[int], Dict[int, str]]: """Detect available controllers and return usable indices and names.""" - controller_indices: List[int] = [] + controller_ids: List[int] = [] controller_names: Dict[int, str] = {} - controller_count = sdl2.SDL_NumJoysticks() - if controller_count < 0: - parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") + count = ctypes.c_int(0) + joystick_ids = sdl3.SDL_GetJoysticks(ctypes.byref(count)) + if not joystick_ids: + if count.value < 0: + parser.error(f"SDL error: {sdl3.SDL_GetError().decode()}") + return controller_ids, controller_names include_controller_name = [n.lower() for n in args.include_controller_name] - for index in range(controller_count): - if sdl2.SDL_IsGameController(index): - name = sdl2.SDL_GameControllerNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) + display_counter = 0 + for i in range(count.value): + instance_id = joystick_ids[i] + if sdl3.SDL_IsGamepad(instance_id): + name = sdl3.SDL_GetGamepadNameForID(instance_id) + name_str = ( + name.decode() if isinstance(name, bytes) else str(name) if name else "Unknown" + ) if include_controller_name and all( substr not in name_str.lower() for substr in include_controller_name ): console.print( - f"[yellow]Skipping controller {index} ({name_str}) due to name filter[/yellow]" + f"[yellow]Skipping controller ({name_str}) due to name filter[/yellow]" ) continue - console.print(f"[cyan]Detected controller {index}: {name_str}[/cyan]") - controller_indices.append(index) - controller_names[index] = name_str + console.print(f"[cyan]Detected controller {display_counter}: ({name_str})[/cyan]") + display_counter += 1 + controller_ids.append(instance_id) + controller_names[instance_id] = name_str else: - name = sdl2.SDL_JoystickNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) + name = sdl3.SDL_GetJoystickNameForID(instance_id) + name_str = ( + name.decode() if isinstance(name, bytes) else str(name) if name else "Unknown" + ) if include_controller_name and all( substr not in name_str.lower() for substr in include_controller_name ): console.print( - f"[yellow]Skipping joystick {index} ({name_str}) due to name filter[/yellow]" + f"[yellow]Skipping joystick {instance_id} ({name_str}) due to name filter[/yellow]" ) continue console.print( - f"[yellow]Found joystick {index} (not a GameController): {name_str}[/yellow]" + f"[yellow]Found joystick {instance_id} (not a GameController): {name_str}[/yellow]" ) - return controller_indices, controller_names + sdl3.SDL_free(joystick_ids) + return controller_ids, controller_names def list_controllers_with_guids( console: Console, parser: argparse.ArgumentParser ) -> None: """Print detected controllers with their GUID strings and exit.""" - count = sdl2.SDL_NumJoysticks() - if count < 0: - parser.error(f"SDL error: {sdl2.SDL_GetError().decode()}") - if count == 0: + count = ctypes.c_int(0) + joystick_ids = sdl3.SDL_GetJoysticks(ctypes.byref(count)) + if not joystick_ids: + if count.value < 0: + parser.error(f"SDL error: {sdl3.SDL_GetError().decode()}") + console.print("[yellow]No controllers detected.[/yellow]") + return + if count.value == 0: console.print("[yellow]No controllers detected.[/yellow]") return table = Table(title="Detected Controllers (GUIDs)") @@ -973,18 +995,20 @@ def list_controllers_with_guids( table.add_column("Type") table.add_column("Name") table.add_column("GUID") - for idx in range(count): - is_gc = sdl2.SDL_IsGameController(idx) + for i in range(count.value): + instance_id = joystick_ids[i] + is_gc = sdl3.SDL_IsGamepad(instance_id) name = ( - sdl2.SDL_GameControllerNameForIndex(idx) + sdl3.SDL_GetGamepadNameForID(instance_id) if is_gc - else sdl2.SDL_JoystickNameForIndex(idx) + else sdl3.SDL_GetJoystickNameForID(instance_id) ) - name_str = name.decode() if isinstance(name, bytes) else str(name) - guid_str = guid_string_for_device_index(idx) + name_str = name.decode() if isinstance(name, bytes) else str(name) if name else "Unknown" + guid_str = guid_string_for_instance_id(instance_id) table.add_row( - str(idx), "GameController" if is_gc else "Joystick", name_str, guid_str + str(instance_id), "GameController" if is_gc else "Joystick", name_str, guid_str ) + sdl3.SDL_free(joystick_ids) console.print(table) @@ -1074,7 +1098,7 @@ def assign_port_for_index( return port_choice -def ports_in_use(pairing: PairingState, contexts: Dict[int, ControllerContext]) -> set: +def ports_in_use(pairing: PairingState, contexts: Dict[int, ControllerContext]) -> set[str]: """Return a set of UART paths currently reserved or mapped.""" used = set(pairing.mapping_by_index.values()) used.update(ctx.port for ctx in contexts.values() if ctx.port) @@ -1107,7 +1131,7 @@ def handle_removed_port( ctx.uart.close() except Exception: pass - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + sdl3.SDL_RumbleGamepad(ctx.controller, 0, 0, 0) ctx.uart = None ctx.port = None ctx.rumble_active = False @@ -1190,44 +1214,47 @@ def open_initial_contexts( """Open initial controllers and UARTs for detected indices.""" contexts: Dict[int, ControllerContext] = {} uarts: List[PicoUART] = [] - for index in controller_indices: - if index >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(index): - name = sdl2.SDL_JoystickNameForIndex(index) - name_str = name.decode() if isinstance(name, bytes) else str(name) + for instance_id in controller_indices: + if not sdl3.SDL_IsGamepad(instance_id): + name = sdl3.SDL_GetJoystickNameForID(instance_id) + name_str = name.decode() if isinstance(name, bytes) else str(name) if name else "Unknown" console.print( - f"[yellow]Index {index} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" + f"[yellow]ID {instance_id} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" ) continue - port = assign_port_for_index(pairing, index, console) + display_idx = pairing.display_index_alloc.allocate() + port = assign_port_for_index(pairing, display_idx, console) if port is None and not pairing.auto_pairing_enabled: + pairing.display_index_alloc.release(display_idx) continue try: - controller, instance_id, guid = open_controller(index) + controller, opened_instance_id, guid = open_controller(instance_id) except Exception as exc: - console.print(f"[red]Failed to open controller {index}: {exc}[/red]") + console.print(f"[red]Failed to open controller {display_idx}: {exc}[/red]") + pairing.display_index_alloc.release(display_idx) continue stable_id = guid should_swap = ( - index in config.swap_abxy_indices or stable_id in config.swap_abxy_ids + display_idx in config.swap_abxy_indices or stable_id in config.swap_abxy_ids ) uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) console.print( - f"[green]Controller {index} (id {stable_id}, inst {instance_id}) paired to {port}[/green]" + f"[green]Controller {display_idx} (id {stable_id}, inst {opened_instance_id}) paired to {port}[/green]" ) elif port: console.print( - f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]" + f"[yellow]Controller {display_idx} (id {stable_id}, inst {opened_instance_id}) waiting for UART {port}[/yellow]" ) else: console.print( - f"[yellow]Controller {index} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" + f"[yellow]Controller {display_idx} (id {stable_id}, inst {opened_instance_id}) connected; waiting for an available UART[/yellow]" ) ctx = ControllerContext( controller=controller, - instance_id=instance_id, - controller_index=index, + instance_id=opened_instance_id, + controller_index=display_idx, stable_id=stable_id, port=port, uart=uart, @@ -1237,28 +1264,28 @@ def open_initial_contexts( initialize_controller_sensors(ctx, console) if config.zero_sticks: zero_context_sticks(ctx, console) - contexts[instance_id] = ctx + contexts[opened_instance_id] = ctx return contexts, uarts def handle_axis_motion( - event: sdl2.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig + event: sdl3.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig ) -> None: """Process axis motion event into stick/trigger state.""" - ctx = contexts.get(event.caxis.which) + ctx = contexts.get(event.gaxis.which) if not ctx: return - axis = event.caxis.axis - value = calibrate_axis_value(event.caxis.value, axis, ctx) - if axis == sdl2.SDL_CONTROLLER_AXIS_LEFTX: + axis = event.gaxis.axis + value = calibrate_axis_value(event.gaxis.value, axis, ctx) + if axis == sdl3.SDL_GAMEPAD_AXIS_LEFTX: ctx.report.lx = axis_to_stick(value, config.deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_LEFTY: + elif axis == sdl3.SDL_GAMEPAD_AXIS_LEFTY: ctx.report.ly = axis_to_stick(value, config.deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTX: + elif axis == sdl3.SDL_GAMEPAD_AXIS_RIGHTX: ctx.report.rx = axis_to_stick(value, config.deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_RIGHTY: + elif axis == sdl3.SDL_GAMEPAD_AXIS_RIGHTY: ctx.report.ry = axis_to_stick(value, config.deadzone_raw) - elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERLEFT: + elif axis == sdl3.SDL_GAMEPAD_AXIS_LEFT_TRIGGER: pressed = trigger_to_button(value, config.trigger_threshold) if pressed != ctx.last_trigger_state["left"]: if pressed: @@ -1266,7 +1293,7 @@ def handle_axis_motion( else: ctx.report.buttons &= ~SwitchButton.ZL ctx.last_trigger_state["left"] = pressed - elif axis == sdl2.SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + elif axis == sdl3.SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: pressed = trigger_to_button(value, config.trigger_threshold) if pressed != ctx.last_trigger_state["right"]: if pressed: @@ -1277,17 +1304,17 @@ def handle_axis_motion( def handle_sensor_update( - event: sdl2.SDL_Event, + event: sdl3.SDL_Event, contexts: Dict[int, ControllerContext], config: BridgeConfig, ) -> None: """Process SDL sensor update event into IMU samples.""" - ctx = contexts.get(event.csensor.which) + ctx = contexts.get(event.gsensor.which) if ctx is None or not ctx.sensors_enabled or config.no_imu: return - sensor_type = event.csensor.sensor - data = event.csensor.data + sensor_type = event.gsensor.sensor + data = event.gsensor.data if sensor_type == SENSOR_ACCEL: ctx.last_accel = (float(data[0]), float(data[1]), float(data[2])) @@ -1359,12 +1386,12 @@ def handle_sensor_update( def handle_button_event( - event: sdl2.SDL_Event, + event: sdl3.SDL_Event, config: BridgeConfig, contexts: Dict[int, ControllerContext], ) -> None: """Process button events into report/dpad state.""" - ctx = contexts.get(event.cbutton.which) + ctx = contexts.get(event.gbutton.which) if not ctx: return current_button_map = ( @@ -1372,8 +1399,8 @@ def handle_button_event( if (config.swap_abxy_global or ctx.swap_abxy) else config.button_map_default ) - button = event.cbutton.button - pressed = event.type == sdl2.SDL_CONTROLLERBUTTONDOWN + button = event.gbutton.button + pressed = event.type == sdl3.SDL_EVENT_GAMEPAD_BUTTON_DOWN if button in current_button_map: bit = current_button_map[button] if pressed: @@ -1387,7 +1414,7 @@ def handle_button_event( def handle_device_added( - event: sdl2.SDL_Event, + event: sdl3.SDL_Event, args: argparse.Namespace, pairing: PairingState, contexts: Dict[int, ControllerContext], @@ -1396,46 +1423,47 @@ def handle_device_added( config: BridgeConfig, ) -> None: """Handle controller hotplug by opening and pairing UART if possible.""" - idx = event.cdevice.which - # If we already have a context for this logical index, ignore the duplicate event. - if any(c.controller_index == idx for c in contexts.values()): + sdl_id = event.gdevice.which + if sdl_id in contexts: return - port = assign_port_for_index(pairing, idx, console) - if port is None and not pairing.auto_pairing_enabled: - return - if idx >= sdl2.SDL_NumJoysticks() or not sdl2.SDL_IsGameController(idx): - name = sdl2.SDL_JoystickNameForIndex(idx) - name_str = name.decode() if isinstance(name, bytes) else str(name) + if not sdl3.SDL_IsGamepad(sdl_id): + name = sdl3.SDL_GetJoystickNameForID(sdl_id) + name_str = name.decode() if isinstance(name, bytes) else str(name) if name else "Unknown" console.print( - f"[yellow]Index {idx} is not a GameController ({name_str}). Trying raw open failed.[/yellow]" + f"[yellow]Device {sdl_id} is not a GameController ({name_str}).[/yellow]" ) return + display_idx = pairing.display_index_alloc.allocate() + port = assign_port_for_index(pairing, display_idx, console) + if port is None and not pairing.auto_pairing_enabled: + pairing.display_index_alloc.release(display_idx) + return try: - controller, instance_id, guid = open_controller(idx) + controller, instance_id, guid = open_controller(sdl_id) except Exception as exc: - console.print(f"[red]Hotplug open failed for controller {idx}: {exc}[/red]") + console.print(f"[red]Hotplug open failed for controller {display_idx}: {exc}[/red]") + pairing.display_index_alloc.release(display_idx) return stable_id = guid - # Determine if this controller should have swapped ABXY based on index or GUID - should_swap = idx in config.swap_abxy_indices or stable_id in config.swap_abxy_ids + should_swap = display_idx in config.swap_abxy_indices or stable_id in config.swap_abxy_ids uart = open_uart_or_warn(port, args.baud, console) if port else None if uart: uarts.append(uart) console.print( - f"[green]Controller {idx} (id {stable_id}, inst {instance_id}) paired to {port}[/green]" + f"[green]Controller {display_idx} (id {stable_id}, inst {instance_id}) paired to {port}[/green]" ) elif port: console.print( - f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]" + f"[yellow]Controller {display_idx} (id {stable_id}, inst {instance_id}) waiting for UART {port}[/yellow]" ) else: console.print( - f"[yellow]Controller {idx} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" + f"[yellow]Controller {display_idx} (id {stable_id}, inst {instance_id}) connected; waiting for an available UART[/yellow]" ) ctx = ControllerContext( controller=controller, instance_id=instance_id, - controller_index=idx, + controller_index=display_idx, stable_id=stable_id, port=port, uart=uart, @@ -1449,19 +1477,31 @@ def handle_device_added( def handle_device_removed( - event: sdl2.SDL_Event, + event: sdl3.SDL_Event, pairing: PairingState, contexts: Dict[int, ControllerContext], + uarts: List[PicoUART], console: Console, ) -> None: """Handle controller removal and release any auto-assigned UART.""" - instance_id = event.cdevice.which + instance_id = event.gdevice.which ctx = contexts.pop(instance_id, None) if not ctx: return console.print( - f"[yellow]Controller {instance_id} (id {ctx.stable_id}) removed[/yellow]" + f"[yellow]Controller {ctx.controller_index} (id {ctx.stable_id}) removed[/yellow]" ) + # Close the UART handle *before* returning the port to the pool so the + # next consumer can actually open it (Windows holds the port exclusively). + if ctx.uart: + try: + ctx.uart.close() + except Exception: + pass + if ctx.uart in uarts: + uarts.remove(ctx.uart) + ctx.uart = None + sdl3.SDL_RumbleGamepad(ctx.controller, 0, 0, 0) if ctx.controller_index in pairing.auto_assigned_indices: # Return auto-paired UART back to the pool so a future device can use it. freed = pairing.mapping_by_index.pop(ctx.controller_index, None) @@ -1469,7 +1509,8 @@ def handle_device_removed( if freed and freed not in pairing.available_ports: pairing.available_ports.append(freed) console.print(f"[cyan]Released UART {freed} back to pool[/cyan]") - sdl2.SDL_GameControllerClose(ctx.controller) + pairing.display_index_alloc.release(ctx.controller_index) + sdl3.SDL_CloseGamepad(ctx.controller) def service_contexts( @@ -1530,14 +1571,14 @@ def service_contexts( ctx.last_rumble_energy = energy ctx.last_rumble = now elif ctx.rumble_active and (now - ctx.last_rumble) > RUMBLE_IDLE_TIMEOUT: - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + sdl3.SDL_RumbleGamepad(ctx.controller, 0, 0, 0) ctx.rumble_active = False ctx.last_rumble_energy = 0.0 elif ( ctx.rumble_active and (now - ctx.last_rumble_change) > RUMBLE_STUCK_TIMEOUT ): - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + sdl3.SDL_RumbleGamepad(ctx.controller, 0, 0, 0) ctx.rumble_active = False ctx.last_rumble_energy = 0.0 except SerialException as exc: @@ -1546,7 +1587,7 @@ def service_contexts( ctx.uart.close() except Exception: pass - sdl2.SDL_GameControllerRumble(ctx.controller, 0, 0, 0) + sdl3.SDL_RumbleGamepad(ctx.controller, 0, 0, 0) ctx.uart = None ctx.rumble_active = False ctx.last_rumble_energy = 0.0 @@ -1565,31 +1606,31 @@ def run_bridge_loop( hotkey: Optional[HotkeyMonitor] = None, ) -> None: """Main event loop for bridging controllers to UART and handling rumble.""" - event = sdl2.SDL_Event() + event = sdl3.SDL_Event() port_scan_interval = 2.0 last_port_scan = time.monotonic() running = True while running: - while sdl2.SDL_PollEvent(event): - if event.type == sdl2.SDL_QUIT: + while sdl3.SDL_PollEvent(ctypes.byref(event)): + if event.type == sdl3.SDL_EVENT_QUIT: running = False break - if event.type == sdl2.SDL_CONTROLLERAXISMOTION: + if event.type == sdl3.SDL_EVENT_GAMEPAD_AXIS_MOTION: handle_axis_motion(event, contexts, config) elif event.type in ( - sdl2.SDL_CONTROLLERBUTTONDOWN, - sdl2.SDL_CONTROLLERBUTTONUP, + sdl3.SDL_EVENT_GAMEPAD_BUTTON_DOWN, + sdl3.SDL_EVENT_GAMEPAD_BUTTON_UP, ): handle_button_event(event, config, contexts) - elif event.type == SDL_CONTROLLERSENSORUPDATE: + elif event.type == SDL_EVENT_GAMEPAD_SENSOR_UPDATE: handle_sensor_update(event, contexts, config) - elif event.type == sdl2.SDL_CONTROLLERDEVICEADDED: + elif event.type == sdl3.SDL_EVENT_GAMEPAD_ADDED: handle_device_added( event, args, pairing, contexts, uarts, console, config ) - elif event.type == sdl2.SDL_CONTROLLERDEVICEREMOVED: - handle_device_removed(event, pairing, contexts, console) + elif event.type == sdl3.SDL_EVENT_GAMEPAD_REMOVED: + handle_device_removed(event, pairing, contexts, uarts, console) now = time.monotonic() if now - last_port_scan > port_scan_interval: @@ -1606,16 +1647,16 @@ def run_bridge_loop( zero_all_context_sticks(contexts, console) elif key == config.swap_hotkey: prompt_swap_abxy_controller(contexts, config, console, hotkey) - sdl2.SDL_Delay(1) + sdl3.SDL_Delay(1) def cleanup(contexts: Dict[int, ControllerContext], uarts: List[PicoUART]) -> None: """Gracefully close controllers, UARTs, and SDL subsystems.""" for ctx in contexts.values(): - sdl2.SDL_GameControllerClose(ctx.controller) + sdl3.SDL_CloseGamepad(ctx.controller) for uart in uarts: uart.close() - sdl2.SDL_Quit() + sdl3.SDL_Quit() def main() -> None: diff --git a/src/switch_pico_bridge/switch_pico_uart.py b/src/switch_pico_bridge/switch_pico_uart.py index 2fdab53..81b2f54 100644 --- a/src/switch_pico_bridge/switch_pico_uart.py +++ b/src/switch_pico_bridge/switch_pico_uart.py @@ -36,10 +36,10 @@ ACCEL_LSB_PER_G = 4096.0 GYRO_LSB_PER_RAD_S = 818.5 try: - import sdl2 as _sdl2 # type: ignore[import-not-found] + import sdl3 as _sdl3 # type: ignore[import-not-found] - _sensor_accel = getattr(_sdl2, "SDL_SENSOR_ACCEL", 1) - _sensor_gyro = getattr(_sdl2, "SDL_SENSOR_GYRO", 2) + _sensor_accel = getattr(_sdl3, "SDL_SENSOR_ACCEL", 1) + _sensor_gyro = getattr(_sdl3, "SDL_SENSOR_GYRO", 2) except ImportError: _sensor_accel = 1 _sensor_gyro = 2 From 0fe53a53b155ab0f62b3c702ff5d9772a1d4b346 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 20:43:32 -0600 Subject: [PATCH 119/134] feat(bridge): add controller-side ABXY swap combo (LB+RB+SELECT+START) Holding all four buttons simultaneously toggles the ABXY layout for that controller with a 200ms rumble confirmation pulse. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- .../controller_uart_bridge.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/switch_pico_bridge/controller_uart_bridge.py b/src/switch_pico_bridge/controller_uart_bridge.py index a5a22af..c1db5b1 100644 --- a/src/switch_pico_bridge/controller_uart_bridge.py +++ b/src/switch_pico_bridge/controller_uart_bridge.py @@ -1385,10 +1385,31 @@ def handle_sensor_update( ) +ABXY_SWAP_COMBO = frozenset({ + sdl3.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, + sdl3.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, + sdl3.SDL_GAMEPAD_BUTTON_BACK, + sdl3.SDL_GAMEPAD_BUTTON_START, +}) + + +def _check_abxy_swap_combo( + ctx: ControllerContext, + config: BridgeConfig, + console: Console, +) -> None: + """Toggle ABXY layout when LB+RB+SELECT+START are all held.""" + if not all(ctx.button_state.get(b) for b in ABXY_SWAP_COMBO): + return + toggle_abxy_for_context(ctx, config, console) + sdl3.SDL_RumbleGamepad(ctx.controller, 0xAAAA, 0xAAAA, 200) + + def handle_button_event( event: sdl3.SDL_Event, config: BridgeConfig, contexts: Dict[int, ControllerContext], + console: Console, ) -> None: """Process button events into report/dpad state.""" ctx = contexts.get(event.gbutton.which) @@ -1411,6 +1432,8 @@ def handle_button_event( elif button in DPAD_BUTTONS: ctx.dpad[DPAD_BUTTONS[button]] = pressed ctx.report.hat = str_to_dpad(ctx.dpad) + if pressed and button in ABXY_SWAP_COMBO: + _check_abxy_swap_combo(ctx, config, console) def handle_device_added( @@ -1622,7 +1645,7 @@ def run_bridge_loop( sdl3.SDL_EVENT_GAMEPAD_BUTTON_DOWN, sdl3.SDL_EVENT_GAMEPAD_BUTTON_UP, ): - handle_button_event(event, config, contexts) + handle_button_event(event, config, contexts, console) elif event.type == SDL_EVENT_GAMEPAD_SENSOR_UPDATE: handle_sensor_update(event, contexts, config) elif event.type == sdl3.SDL_EVENT_GAMEPAD_ADDED: From 0f037b0a44b4f30879699ee3f6f9d1450e26fbcf Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 20:43:32 -0600 Subject: [PATCH 120/134] feat(bridge): add controller-side ABXY swap combo (LB+RB+SELECT+START) Holding all four buttons simultaneously toggles the ABXY layout for that controller with a 200ms rumble confirmation pulse. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- .../controller_uart_bridge.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/switch_pico_bridge/controller_uart_bridge.py b/src/switch_pico_bridge/controller_uart_bridge.py index a5a22af..c1db5b1 100644 --- a/src/switch_pico_bridge/controller_uart_bridge.py +++ b/src/switch_pico_bridge/controller_uart_bridge.py @@ -1385,10 +1385,31 @@ def handle_sensor_update( ) +ABXY_SWAP_COMBO = frozenset({ + sdl3.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, + sdl3.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, + sdl3.SDL_GAMEPAD_BUTTON_BACK, + sdl3.SDL_GAMEPAD_BUTTON_START, +}) + + +def _check_abxy_swap_combo( + ctx: ControllerContext, + config: BridgeConfig, + console: Console, +) -> None: + """Toggle ABXY layout when LB+RB+SELECT+START are all held.""" + if not all(ctx.button_state.get(b) for b in ABXY_SWAP_COMBO): + return + toggle_abxy_for_context(ctx, config, console) + sdl3.SDL_RumbleGamepad(ctx.controller, 0xAAAA, 0xAAAA, 200) + + def handle_button_event( event: sdl3.SDL_Event, config: BridgeConfig, contexts: Dict[int, ControllerContext], + console: Console, ) -> None: """Process button events into report/dpad state.""" ctx = contexts.get(event.gbutton.which) @@ -1411,6 +1432,8 @@ def handle_button_event( elif button in DPAD_BUTTONS: ctx.dpad[DPAD_BUTTONS[button]] = pressed ctx.report.hat = str_to_dpad(ctx.dpad) + if pressed and button in ABXY_SWAP_COMBO: + _check_abxy_swap_combo(ctx, config, console) def handle_device_added( @@ -1622,7 +1645,7 @@ def run_bridge_loop( sdl3.SDL_EVENT_GAMEPAD_BUTTON_DOWN, sdl3.SDL_EVENT_GAMEPAD_BUTTON_UP, ): - handle_button_event(event, config, contexts) + handle_button_event(event, config, contexts, console) elif event.type == SDL_EVENT_GAMEPAD_SENSOR_UPDATE: handle_sensor_update(event, contexts, config) elif event.type == sdl3.SDL_EVENT_GAMEPAD_ADDED: From 2b3e4f3d4ce6d366419993783fe51ebc87aeb012 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 14 Aug 2026 21:35:13 +0900 Subject: [PATCH 121/134] Update to build uf2 and elf with build.py --- README.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++--------- build.py | 10 ++++++++++ 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 01b46e6..4ea511b 100644 --- a/README.md +++ b/README.md @@ -103,19 +103,62 @@ Filters you can use: ## Building and flashing firmware Prereqs: Pico SDK + CMake toolchain set up. -### One-shot build + flash (picotool) +### Using `build.py` + +`build.py` configures CMake, builds the firmware, checks that both output formats +were created, and flashes the ELF with `picotool`. + +Before running it: + +1. Install the Pico SDK, CMake toolchain, and `picotool`. +2. Connect the Pico in BOOTSEL mode. +3. From the repository root, run: + ```sh python3 build.py ``` -- Requires `picotool` on your `PATH` (or set `PICOTOOL_PATH=/path/to/picotool`) and a connected Pico in BOOTSEL mode to automatically flash. -- Set `ELF_PATH` to override the default `build/switch-pico.elf`. + +The generated files are: + +- `build/switch-pico.elf`, which `build.py` passes to `picotool`. +- `build/switch-pico.uf2`, which can also be copied to the Pico manually. + +To customize the controller grip color while building, pass one of these mutually +exclusive options: + +```sh +# Use a random color for both grips +python3 build.py --random-grip-color + +# Use a specific six-digit RGB color for both grips +python3 build.py --grip-color FF00AA +``` + +Both options update `controller_color_config.h` before building. With no color +option, that file is left unchanged. Run `python3 build.py --help` to see the +available command-line options. + +If the tools or artifacts are in non-default locations, use these environment +variables: + +```sh +PICOTOOL_PATH=/path/to/picotool \ +ELF_PATH=/path/to/switch-pico.elf \ +UF2_PATH=/path/to/switch-pico.uf2 \ +python3 build.py +``` + +`PICOTOOL_PATH` selects the flashing tool, `ELF_PATH` selects the ELF that is +checked and flashed, and `UF2_PATH` selects the UF2 that is checked after the +build. Their defaults are `picotool` from `PATH`, `build/switch-pico.elf`, and +`build/switch-pico.uf2`, respectively. ### Manual build ```sh cmake -S . -B build -DSWITCH_PICO_LOG=OFF cmake --build build -j ``` -This produces a `.uf2` you can flash (typically `build/switch-pico.uf2`). +This produces both `build/switch-pico.elf` and a flashable `build/switch-pico.uf2`. ### Manual UF2 flashing (BOOTSEL, no tools) If you already have a built (or use the pre-built one in `firmware/`) `.uf2`, you can flash it without rebuilding: @@ -131,11 +174,6 @@ Flash alternatives: bootsel + drag-drop or `picotool load`. Flags: - `SWITCH_PICO_LOG`: enable/disable UART logging on the Pico. -### Changing controller colours -`build.py` can optionally update the **grip** colours in `controller_color_config.h` before building/flashing (default leaves the file unchanged): -- Random grip colours: `python3 build.py --random-grip-color` -- Set grip colours: `python3 build.py --grip-color FF00AA` - ## Python bridge (recommended) Works on macOS, Windows, Linux. Uses SDL2 + pyserial. diff --git a/build.py b/build.py index dabefe8..5d96461 100644 --- a/build.py +++ b/build.py @@ -14,6 +14,7 @@ CONFIG_FILE = SCRIPT_DIR / "controller_color_config.h" BUILD_DIR = SCRIPT_DIR / "build" ELF_PATH = Path(os.environ.get("ELF_PATH", BUILD_DIR / "switch-pico.elf")).expanduser() +UF2_PATH = Path(os.environ.get("UF2_PATH", BUILD_DIR / "switch-pico.uf2")).expanduser() MACROS = ( "SWITCH_COLOR_LEFT_GRIP_R", @@ -120,6 +121,15 @@ def build(): ) run_cmd(["cmake", "--build", str(BUILD_DIR)]) + missing_artifacts = [path for path in (ELF_PATH, UF2_PATH) if not path.is_file()] + if missing_artifacts: + missing = ", ".join(str(path) for path in missing_artifacts) + sys.stderr.write(f"Error: Build did not produce required artifact(s): {missing}\n") + sys.exit(1) + + print(f"Built ELF: {ELF_PATH}") + print(f"Built UF2: {UF2_PATH}") + def flash(): picotool = resolve_picotool() if not ELF_PATH.exists(): From 9ef5cb38134b5195e95a53f87f4c2bc51074b1ca Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 14 Aug 2026 21:35:13 +0900 Subject: [PATCH 122/134] Update to build uf2 and elf with build.py --- README.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++--------- build.py | 10 ++++++++++ 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 01b46e6..4ea511b 100644 --- a/README.md +++ b/README.md @@ -103,19 +103,62 @@ Filters you can use: ## Building and flashing firmware Prereqs: Pico SDK + CMake toolchain set up. -### One-shot build + flash (picotool) +### Using `build.py` + +`build.py` configures CMake, builds the firmware, checks that both output formats +were created, and flashes the ELF with `picotool`. + +Before running it: + +1. Install the Pico SDK, CMake toolchain, and `picotool`. +2. Connect the Pico in BOOTSEL mode. +3. From the repository root, run: + ```sh python3 build.py ``` -- Requires `picotool` on your `PATH` (or set `PICOTOOL_PATH=/path/to/picotool`) and a connected Pico in BOOTSEL mode to automatically flash. -- Set `ELF_PATH` to override the default `build/switch-pico.elf`. + +The generated files are: + +- `build/switch-pico.elf`, which `build.py` passes to `picotool`. +- `build/switch-pico.uf2`, which can also be copied to the Pico manually. + +To customize the controller grip color while building, pass one of these mutually +exclusive options: + +```sh +# Use a random color for both grips +python3 build.py --random-grip-color + +# Use a specific six-digit RGB color for both grips +python3 build.py --grip-color FF00AA +``` + +Both options update `controller_color_config.h` before building. With no color +option, that file is left unchanged. Run `python3 build.py --help` to see the +available command-line options. + +If the tools or artifacts are in non-default locations, use these environment +variables: + +```sh +PICOTOOL_PATH=/path/to/picotool \ +ELF_PATH=/path/to/switch-pico.elf \ +UF2_PATH=/path/to/switch-pico.uf2 \ +python3 build.py +``` + +`PICOTOOL_PATH` selects the flashing tool, `ELF_PATH` selects the ELF that is +checked and flashed, and `UF2_PATH` selects the UF2 that is checked after the +build. Their defaults are `picotool` from `PATH`, `build/switch-pico.elf`, and +`build/switch-pico.uf2`, respectively. ### Manual build ```sh cmake -S . -B build -DSWITCH_PICO_LOG=OFF cmake --build build -j ``` -This produces a `.uf2` you can flash (typically `build/switch-pico.uf2`). +This produces both `build/switch-pico.elf` and a flashable `build/switch-pico.uf2`. ### Manual UF2 flashing (BOOTSEL, no tools) If you already have a built (or use the pre-built one in `firmware/`) `.uf2`, you can flash it without rebuilding: @@ -131,11 +174,6 @@ Flash alternatives: bootsel + drag-drop or `picotool load`. Flags: - `SWITCH_PICO_LOG`: enable/disable UART logging on the Pico. -### Changing controller colours -`build.py` can optionally update the **grip** colours in `controller_color_config.h` before building/flashing (default leaves the file unchanged): -- Random grip colours: `python3 build.py --random-grip-color` -- Set grip colours: `python3 build.py --grip-color FF00AA` - ## Python bridge (recommended) Works on macOS, Windows, Linux. Uses SDL2 + pyserial. diff --git a/build.py b/build.py index dabefe8..5d96461 100644 --- a/build.py +++ b/build.py @@ -14,6 +14,7 @@ CONFIG_FILE = SCRIPT_DIR / "controller_color_config.h" BUILD_DIR = SCRIPT_DIR / "build" ELF_PATH = Path(os.environ.get("ELF_PATH", BUILD_DIR / "switch-pico.elf")).expanduser() +UF2_PATH = Path(os.environ.get("UF2_PATH", BUILD_DIR / "switch-pico.uf2")).expanduser() MACROS = ( "SWITCH_COLOR_LEFT_GRIP_R", @@ -120,6 +121,15 @@ def build(): ) run_cmd(["cmake", "--build", str(BUILD_DIR)]) + missing_artifacts = [path for path in (ELF_PATH, UF2_PATH) if not path.is_file()] + if missing_artifacts: + missing = ", ".join(str(path) for path in missing_artifacts) + sys.stderr.write(f"Error: Build did not produce required artifact(s): {missing}\n") + sys.exit(1) + + print(f"Built ELF: {ELF_PATH}") + print(f"Built UF2: {UF2_PATH}") + def flash(): picotool = resolve_picotool() if not ELF_PATH.exists(): From 927ceef4b02e8aacd3d6527fa44c2a44768aa97a Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 14 Aug 2026 21:37:24 +0900 Subject: [PATCH 123/134] Update uv lock --- uv.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/uv.lock b/uv.lock index c8f27d1..ec7899d 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.9" resolution-markers = [ "python_full_version >= '3.10'", @@ -468,7 +468,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "mdurl", marker = "python_full_version < '3.10'" }, + { name = "mdurl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } wheels = [ @@ -483,7 +483,7 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "mdurl", marker = "python_full_version >= '3.10'" }, + { name = "mdurl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } wheels = [ @@ -804,16 +804,16 @@ wheels = [ [[package]] name = "pysdl3" -version = "0.9.11b0" +version = "0.9.11b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "packaging" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/2a/86e2b1cc7ac5a186ac3bad262cfbe55313268b6a8f7a3e61a4212fa4af09/pysdl3-0.9.11b0.tar.gz", hash = "sha256:e87dce397221c4943763bb45ec8725deb07b2bf5636d5da875e9d0ae34504068", size = 1421769, upload-time = "2026-03-15T19:28:04.222Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/71/1de90f3baf555003b97e8c09db2f40376aa44105a2f2f98e239260cd07b4/pysdl3-0.9.11b1.tar.gz", hash = "sha256:1608b1e979fd6693b321af38e1d70a8a1df8141753096ade3477ff2b437c6ed0", size = 1421849, upload-time = "2026-05-06T23:50:21.42Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/ff/58a5b5a5bbf1fb822359c8fcf1a307d9dadc078d988cd6e770f4381081bf/pysdl3-0.9.11b0-py3-none-any.whl", hash = "sha256:98bd71dd4e1bcdaa5e83da200a208439048690da627b2d19163f5e222518cb24", size = 99667, upload-time = "2026-03-15T19:28:02.81Z" }, + { url = "https://files.pythonhosted.org/packages/a5/5f/290b001a4f46a3811caf103bd98fb23bae0c700ab231fde8e1d6bfe2ecdf/pysdl3-0.9.11b1-py3-none-any.whl", hash = "sha256:2dd0bfe859ae93564b38c97f718d1310dbb4ea898fe85baea499f03012b1a152", size = 99721, upload-time = "2026-05-06T23:50:19.819Z" }, ] [[package]] @@ -897,9 +897,9 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "idna", marker = "python_full_version < '3.10'" }, - { name = "multidict", marker = "python_full_version < '3.10'" }, - { name = "propcache", marker = "python_full_version < '3.10'" }, + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, ] sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } wheels = [ @@ -1042,9 +1042,9 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "idna", marker = "python_full_version >= '3.10'" }, - { name = "multidict", marker = "python_full_version >= '3.10'" }, - { name = "propcache", marker = "python_full_version >= '3.10'" }, + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, ] sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } wheels = [ From 76c1f7c2d2723184e2208c27c69d6fc67513e439 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 14 Aug 2026 21:37:24 +0900 Subject: [PATCH 124/134] Update uv lock --- uv.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/uv.lock b/uv.lock index c8f27d1..ec7899d 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.9" resolution-markers = [ "python_full_version >= '3.10'", @@ -468,7 +468,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "mdurl", marker = "python_full_version < '3.10'" }, + { name = "mdurl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } wheels = [ @@ -483,7 +483,7 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "mdurl", marker = "python_full_version >= '3.10'" }, + { name = "mdurl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } wheels = [ @@ -804,16 +804,16 @@ wheels = [ [[package]] name = "pysdl3" -version = "0.9.11b0" +version = "0.9.11b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "packaging" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/2a/86e2b1cc7ac5a186ac3bad262cfbe55313268b6a8f7a3e61a4212fa4af09/pysdl3-0.9.11b0.tar.gz", hash = "sha256:e87dce397221c4943763bb45ec8725deb07b2bf5636d5da875e9d0ae34504068", size = 1421769, upload-time = "2026-03-15T19:28:04.222Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/71/1de90f3baf555003b97e8c09db2f40376aa44105a2f2f98e239260cd07b4/pysdl3-0.9.11b1.tar.gz", hash = "sha256:1608b1e979fd6693b321af38e1d70a8a1df8141753096ade3477ff2b437c6ed0", size = 1421849, upload-time = "2026-05-06T23:50:21.42Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/ff/58a5b5a5bbf1fb822359c8fcf1a307d9dadc078d988cd6e770f4381081bf/pysdl3-0.9.11b0-py3-none-any.whl", hash = "sha256:98bd71dd4e1bcdaa5e83da200a208439048690da627b2d19163f5e222518cb24", size = 99667, upload-time = "2026-03-15T19:28:02.81Z" }, + { url = "https://files.pythonhosted.org/packages/a5/5f/290b001a4f46a3811caf103bd98fb23bae0c700ab231fde8e1d6bfe2ecdf/pysdl3-0.9.11b1-py3-none-any.whl", hash = "sha256:2dd0bfe859ae93564b38c97f718d1310dbb4ea898fe85baea499f03012b1a152", size = 99721, upload-time = "2026-05-06T23:50:19.819Z" }, ] [[package]] @@ -897,9 +897,9 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "idna", marker = "python_full_version < '3.10'" }, - { name = "multidict", marker = "python_full_version < '3.10'" }, - { name = "propcache", marker = "python_full_version < '3.10'" }, + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, ] sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } wheels = [ @@ -1042,9 +1042,9 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "idna", marker = "python_full_version >= '3.10'" }, - { name = "multidict", marker = "python_full_version >= '3.10'" }, - { name = "propcache", marker = "python_full_version >= '3.10'" }, + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, ] sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } wheels = [ From 4f8177f87f66f4f136725352a7387e5f8217ca27 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 14 Aug 2026 21:39:11 +0900 Subject: [PATCH 125/134] Update firmware --- firmware/switch-pico.elf | Bin 644580 -> 763208 bytes firmware/switch-pico.uf2 | Bin 91136 -> 69120 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/firmware/switch-pico.elf b/firmware/switch-pico.elf index fe40183825665e00cb9d03df88c1f898be5b6400..2d78548492b723709c2f5a373ca7e6df1011c270 100755 GIT binary patch literal 763208 zcmeFaYhYB>)iAuynM)>K+HAo z9s|Xs+9&v`~&AiNfE0zoLb1Hf%I(42QlA8}2vUPq(07+amA)S(pd6(sc6XQOH3|fL< zQvZN_Q@+847L@??heM7l{)zcjOUDBIepsf?Q18?`hT7drN!tOmz0^(|v(XSci!y4j zxyJ6|UK)kRL!05=cubUe^qEe4DSz!_|B{9J&zJwX|~N-H9~B!gZk2HScM>k9&)uAe^nS9Zg}p zbTDV+f~UbFaz2BtVsVboP+DQJ`V7Y_e1;1cY}+vhL&Mg%G_~u##Wm$doV~(BW$A^Z zj*ws380o{SV1vz{ad&ddxg}hnxvo(eY#5@0E5bciPhPBo3a$;)R)j;a6`zCx-U

p=bly6rQ7;G679O79Q) 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 z z%Vr=)?NyR|&5^7pIxdlOaSq4V%(@Yjo;2h!;g!7e%hk@Aw9`jUCSs07^EiOyH_$~p zIF@Yj65-NXm^sWv^S}Pg&@mKdk$_9L&MVc$ z93Y!A#U#2s?Vk|MgS|!)<8bi~g=h@0;I!!{6yuh3JBYdKfdC zyZMnG#edxBVa4xd;Kzr-cb6i8gVm^S83TvSg_~`M0>QXUW#y zaK)B1tS6{6R~PWEsWe5`Q1q&z=<8MVn^u(4tM~MVS-0^ee(%1m($iDvsch=qv}rM) z75k6BJ$;qR5J1Mt9@j|Qb&Yir}uhQ z|IdDVLkvLw_Ze6lDi|x&e^4ks-n2Zm#rs&qUkb3PSn(o$;iIQg)TERew14TLpwEL% zMa}p=wrZo)BW}Q}Hg27N#4!ixO$Scf={t4Lu0+x~KB9iCK-8ZKQSDD^?LXQ|^xV|N zf47zNtmyaSePA9DHw>|}{PHmMPJ7Ezy`r*}@rC{^;jUZn41{8}LndhY($!RWSvwWH z=*M!ka_s?liTwHI#k3%J$gaNW+eG!;$j2spxqs`kA_B zi}uqO!+^#7N+YrE{QnUff*hmCX z2_+v|-0zxP!_Yq1T;q?MAN)yb!U4ly|Dtw{Ch$|OH#GbG=2m{#{NN|e51yTsiYC=X zqBjlgz9DM@+=rSUe5Coo$JFlPUXFY8HbC<;-d|6cey7GC4m5I>hmZaG)xK#L8!Uv6 zmBp`c$k?OBmEK;p${%4v`j4$Z_Q5rr*j=}-VKAZ#{9B?wBAGAueK=!_4ei&eVMgN~ zdfmh(IJ}|x!Mzjxiopj_zeC&CV1VOdB-?fH=#KrPSz||twI>zQ|MP3p9ao(xNzf)% zNAKV`%IC+j4!5>nj~VV9`ZFvrDS8|J+imXQaS1# zlHcUkjCj_E2b`?VH+AZdMGDh1VT0DQ>V&6z;0pa*WI?Pm31!@z_3GW(*{ls&?ZwdpoAD#fQjhA#5r5LO_ZHvkM|gZeX0JP!=D#zc)bk%! zzL;hGkf;)77qV-`g+_+3+{dAc$jXz%T})(79VX==J@)xh(Zt%iIjaREX?^e~JgwX) zP2M@Nn)^?ndh-wpJ;k$ zBl)ZzqB42IsgbhE|Gi!CoM%eaAxqVac=sQ3F*OEqC9f!F5sC-%mz#1J5R_Gx?8*ZA;Dqc-uc zdSk1n5gRJ{%rz(zyR>9WCTV^BpH{H`=X-7~_lB>w-7P_if7VLfr=CSg)0))pv`y{1 z{xI>yaTNS2x@M20=2S}0?T-tEu^>RH};3+en=PyzZvD9VajKa29Dps?L`M4Q3v)X;ghMm zVMh6yFm($(Av#~P6$SQDTVNGinx*D{DwQ;vb0eH0ItdgJ;_*vQ0~(VdMKEqsA>U(9 z8w!4?Wx+pfZ_-z7=|68z8>&k$PXC%Z`>wxgDE=dR7P*7!+2EOo$c{e3j)EFkm%kTE zsrlm5Cmd%lMB-~hFMkzEw`Gy4rE-Kxph4|Ty5hEDX{&991dph)S!k7Ln@RCh zOhHmSdEBgQE)oYi-&kByHZ8qky|SrW`{qQ&Fe7PsR6MzG$raCDay4GvtZ$-EK9RdB za?-fy1clC-a*>f*xiFiyK3lme{oP#qep;BJQ^Q>G5vKJgT{St^&r)4tZr5a+Xzy|| zHIgr`Ll1RqZ57n{6|mJ4VZ)yj8@4|H;U@yZ8AgGjYWC0QkI5E=UTM5zeMuBY|CX2t z8|jn?mPlexW9jdgp@Qid-^2a4RzsyqhMqbrwhiKsIOaxKRb0_P z!X84s#a!2D`}YpjxcABBKMj@cXfN3-FBc2NA8|N(7#nC}t{+q7+CS&p@CD9dS|=Nj znM2zF6Mvd~cPF#kuHU{NkLf5b=~f$k|Gpi)dv@;Z-G2D`RR<0p*u84^_8t3rSFfwC zsjg0@s!^J2-1m>1;cRRa#AojIDU46Mb`QCADmj*6T4~$kfNA53Nls>80A63{QIDSQ zsjNLy^)ygr<-_U77v%jOyQu7=SE%SW>wBC8+joS_$((U%6 z9iSR#+-}ue;}(fg5u@ylG1u?ixpHOPZsXWg({>)-d-VDpy*He@>iXUT2Zs*z?$|es zBmBVbXJESBzk8UQlMfwIxIq2B;XAZ}J;cV@=|2=X%y4%eJTSCRwpxoGRyf6rwffT{ zW?Do=JVlok$CGBYdTR_;Xx**172+*)*G6yEw^0#4Y;nnF^Hv8+E{Jb8rY&)X>R!xS zvA(O~+aj&8<+Hw&w^#7?SN-kZGu6=cmGMH(Bg5Md?Am^K*R~z|4s1Vs6H?63&(|rX zu`rSb@?D!pVgbDpJHeadP4HxR0%$i9fKJFqxE3HQHP-@|>9N5_5AOyY!OleNg_#?< z20OGJqQPS*tNcNd${;?!lWTLVUs`2-$ z@w<76&cbx;<^$1d>V*7q6q8D3vQzF^@aoR@sa5lT*)uCt`!mEC&r~w`srhc&U8|#x ze)D|k5*odkALaZ>7BXBWollh$fF-mU(lc@a@P&-aavTcmZvShKlY6m5Cn6>_aKMkyQOh-s<9RbH46vAUp5r{N8w=v4r`B{VQ!=%hhU;nJV&5Uj7@ z&jg{%#4^Y8`S7cgiIC@`k+r~F%OP0B^9-K88nBAdG*_!(UUGsnokK0$(G8kVjB7uyGyiPd;LvLb+Y_DKG!YT^Z_2Ponrta*OlH2 zCYvnhP8X}Ap$=G%ur>0VZ}vN$&l=3G9LkqVRYV~i) zX5Y!rJ$Cw0pGtwVa&S<6tXKZ4{;X%qZQNU(4Gd+E8p>7=W@is&%Z9QALs>VQL~Y$3 zH`<SWeQ9XpaZygeHzMZYGxa{}Huc6}NxKG!s z<*IKYr)a+GwII(`Q`&Bxtmr&|uY2g*@w8;KeLwZZ0bSk6^k!vU6`CegC{gzHHW*PC z?QyonLahcj0Q6BVk%|F>%#5l|UT~$|6bnI^pgVadrTw*D)pCt*g6QmoGWV^nqs1A6 zT9ALz9EOalT;tQ9@7>JG2Z(ea0|`t*Pr~%}b4&VS@M2Z?-oBJ$G<@Xi>~A?klY*|W4}q2o4( z&*bOu>l}MIU3u67I4(GKDhIH)@^vsEnlAkb)YQ}}LS-w}}6At$F*wI?M zAc#$J8Z1|Han2?ZB4#pu%NB}O99=c=A&2g53}ZC|o*uS~GAVRLM~0??WBRmI#=_q| z@l9Ab_ry=@tr*Sg8q7{Fi65nW zt_P$vV${6fB#L&w^S<5Z!69W?K3B(~KsoAU3{W=z)wr=z2O4Z*O~teJZpjV%!N>a_ z4L-h`J)&iOHz_qV%vs?(rG9a}@H@8<`0wk>&jC)mPUZT`j;kX?&auuqNTa63=!Oj_ zZb)gn;-`{o%p&_U+1UO^-f*|#9)&4|g+LQOkoceFIjn}B+dz{=o;MCe`G8+j%ZkDi zgmB@LTyHs(MHXwHv68~LS-m(mX?{V@m2|HIdIMy352|4@+D&6_5+SDnCT&74la0T7 ztf^V~qwTlywJm3N7`fqOg-Z}y7*n`nOiIKTK5AY9Ht zz-umqxIIme-aaTX07giEVP%!CPgHuk&rQb!9E(8wqV0lZZG7ngI)#`)UHA}O7tFmj< z=zz7%AEV^$4^r@Sb*;z4>0N2kX*^$&o;!Qi%P(e|&dhe4oo!MBH|Wn6jSFlPXoMw< zPX%>F_z#M~F4ZG3Z*$9d389>*ZGtJn_JtG+=m{6ur`p?k?AM!Gc1H$xVMAAcw(tVl zSZ=9MJwQ?8Fc#d;e()iCHd+{VHV=0YoMOS{**x?wxQtpUvqKlIWYg*X*Osq6Ya;hy z|Kynzx1l4Ys3#hWcgOi})GxiR+AlOG<&ogBS<@?Cn)wa&U0Xnlw2Rhj2ymQgKf0QS zOd2!yDX0sL>zo#Y&KC5Wf+s;#T0r|+nynZlx_Q)tmZ=1&JuY5&5eGbcqH7IcGd`}N z6#x>4oR_a?(VK7Cj*>Q-mTfacb58bTP0f^~8Uj7Y+ocIF;0&|*FKuYJLr=cg*hR#0 z9zc-4a8vnqgm57376D7`@hk8x zY+7yM`;Fxwenmk`;juLp{BbO3+AujeF&OmvDc_wGxo$H&@V{e`0j-&@EsR)tc1o~& zH(egzwQzyi*yLI`!nczBp^pbjQZm|0y~}#cO=ij=&%dSTZwe(j3VXw8WjtrgDqNCi zznAh5Cs{f#v;+qPjlSp^0ZB88y_p*PHf&7aEEsv z9@@ThG%H=BNy`JH%|Oce(Cx!R2M=H0Xoeb#Iji3Os$?i*X<_Wv!V|YTo`PR+?87=S zBj$ zxYG2Bv5j(FmE1=So2Av!0M&ID(g;gFI~L%%n-@{ZA7k*+wUqOaWq~or0g9kyV_~K-5R4*Ojx#!aw0nN{ z*q!H-oOQxfkjztNC)kOk^y?-5Bgk_@O4-wrZ=nZc7oSOv<*18QNI9{{uQciGCEr%G$6l2TOH@wIzM%poRe%cVwLm)ge*Y=f*6x}GGtRn_6_LIg7I z)TIsUa*v?U&?SndGIY0VM(3p$|LQMHKcaf6;_n}m-g6vT`jfg#>Mbe|YIBLi z+Kb9Qegzj8{D~JzrFHBZJ0Ac+Dk%e6X-=gjXwTHLF+4ui!X*$#UN?-OVahaV4ZnyV z81C!dTN6dva6AXC-d~yOb27Bm`yxT)D~lm;ejRyWz)idIT3G*F++?S20KUeWz%+;V z4+ynipXww0P;@)bAJp@=#OIq#<~GS_k|erGL)KDNQfPaA9)EK^=CHlVKl9Y@71X!v zh&^_2-qPGhl(Ov+xauICs((KkjJ93)a$C6%?+Nn0_DH4$h{>#^u>@ph8M7E-Dc|5X zN)B*ML};r*G{(hoXw+;>T%n##iC#UT^t&}*X~nz$M?`@Wmrzbb9B4JImi2o_7fBj9 zW&K+HyoeNubz4}ipja8I4mgpVIE}DsmUhM~p;tqeu<;Y2iW!evO0Rlk zQKJpZGA7bcw;;Bcl3y*7>Om@8g5@TAy%>(lF?2@1w|nay034C5q>Q2qUJ0g zA=@F(QGbL39g13$=Me6a$kr%`?K-^g2I~cRYeb^4MzL%6;ll?t9vY~LX;AO_L}A?X zqZ~5K+JZ#5>gW2S!@F;!7GHPy&6ZRt-1#%>L_r2@a4-N-W0ro$gQ~o1cKbgOCbe8l z>VnAhD=xypKK;Z+_N$aQPbJnngWqgI08+v9by>&4a=yTf${msqDsZKC3=h+=W|-Zy zjE%)^f{^u_TP$vhfyZz(PrfUEb%%IaKO&q&L|i#?p<<4arQZ#;X70*n;Ymk?nOlf3 zvSoLeL#8IyAh6IjwjvJ6E{!)vUdjYMs0q9;@^SZux?GcSsWQeP)sbU;C10_Y>9UUY z{4s7wldHm}gB4MOlKGU=uy)o~7UvD--%@HYlQSPhcRx{gABo)&NvWaIp9E#uTo&N7 zmcgPT-oJp$cU(|eOwIXMQd3tg>!9$W@J*NO7I7V@WCKYKmN%Gq(M^`WOoJ87E3=bR zwKv=8c13$E2}M^cWlTmQZ3)MN`#ECY4kI7OPQkQd*2i|Bc(y+U4xr9tFA?CWD+LqnaoSkJzN0Ou*h!UW-Vn$1v&W~ z&={&7jDUe&s}JB<_2#9%6p_9w{F&rPT|D}9eWq3DEmRyCG^3e2I=&?MPf)6)uJDmt zK;sTMN5es>E8RS^(3oZMo>6I9HiH)?{81iLgZEaOt|N=E`ny^!q+T0^{IgS(Id{1R zg6#}9+>N%;462U1X+tUL(;R`IV%RUIS(f~@zC8%lG3k9S{gfX^KS;qsmtx7?<&K&1 z%wi?u2KI;zw2PcQF+rlVFx%$z(qao5#6c}?+Ud2p;HYSxLD7xs?@!g=lCIvs9bARV zvmiC}b-i8M*YvtjHU2C0W2>XBl!q7CWfE*E-LYBQOC%{DeJ@Sd+`?~{44<_Iy>YMM zyp%_w)T>*1XXzkiS?eo1X-PN4>T}R@DJl`6P5$}CjZB4I^U>+ui}cRjdImn6DiN%7 zDg9K+oAz31xv&4_spe~!7@rvyfGD(scvGXC^>F12GpZAxb*ojf(OJ1eiVZU*Gq=`irqbE*NP9u z+XdeU6Ex$TOs1=0H^h4!;Jb=n1M@M zH!zq>HJBIIgkR5Y8m*ue>V_wI)F-+PsZO2CE4X@>Dy9oN(YiWn5#>=bC9vi+b&fki zVKs|l5TGs$WBu+WIHGBGh;-em5dK3wTt#N7xBprb1Xkf~6qcD6QPKw2j%nTO$5I3y(3IZbl6OJ#&6{p`pJ0P835~feIPV|== zwu$>OHO<^YQO)jOlJVc;Uh+!&WHl<+%|mK*2_x$1?CRDH z5vBYYUb|3sJ}xUjPpy>Kz1Y^Vls~WCq9_VC(PvwhzNoSE6^zW3lK+a+_25HhX)wB2 zaU5N4&?pqrZ&<^fUET6}UG17(aUAY@_L3KS9eIjcKWu#Q1Uuq%8QkK81#QWF!}|{0 zMEd>SBZqhP9y)krCPG} z_BVj;)IBL`u3?VFwlwEL>q#}LlXY9Np{2AQqX^`?cJ6A(R5=WND?&>k()PO$W_jQw zZkyjig0q7Lka7{$ldl6Ay|}If5q*SQ5Ahu#*CU75;J6t1E7IiHdMG11`~z{DE}Y}Ug$LwxOv z@B93HmS1BPOB^_mgb|*K*TXC77tRHVRugGXw_y__KOc#qbOle&sC&|WnnVoQf$#rS zL=1!k3TuqWorp)(Jp4;rIeS7WtIpEjYb4EFFA>F}Wg|3RiJULN5)CCpuT4PGH_=CJ zAxdgU&nWw4T0!;A;M?zFlb}E0@pSL;9|Fnw;R7^!D&SXN0y|EN5;i)4v9wn3UEqw|ln}<48;#M6OaAbrX zZf`4xPXW(>Gf4*w_~yc&g%0cRj3n5#v=j0RvU#z+b^FXSM5sxJ7Gnb~y&L(Q3e-3rWC%;9Le-{Ii zuE+VPb1BYVR9!#c)aBP0aqwsESpujW3)*KU!E*}0%jj`UY)*ET&20Q-e^paStl@Zlq7DjxYWo$xB#4@q zicfAtJwH6(Hm^;0GEw=!N!f}O*M|plA}a7D$pbi%;pzEg4Wi1swQRu^=3WV1`4B*0 zMLyA1;x@)wDd~c)6IuwjEd*8o!8Kf_wKVLk2kRz-C*wHRT(yG&>f8j>z)>*=@4AN1 z(rTT;4p(E6>58y)-~snuI$IfDXY)+G+2Lwh?zMpZw1{*{G@rT1_&Q4`$h9g1Hd>|8 zYBAh2+8-AcuWYFH6T{fl?||w4^qZ=Xm#oSwQUH{@pel8DwoeqQHQ(7zD|fYPV4dzut1_-(LE*R4 zxO&;EIHrs-EfgXSy7JtcX7JkMl^mqx>(%D(rjl#8wYO-rcC;b}s>)#YkjlTs%2y$q zp>vH)BTQN}i}4QlfH1!+M1p z|FHFJLJu=ZGKWOu#Ly1~_Ip*!)t>ir{0z>`xu+2)k^mk0ecc4K38$%V4V%z*4&qK?-hket5ecO)rQLwilzAjmA8| zQR_8Z!Mi`)=QmI!AJh}O9-P$BG6yLTV-9KE|z_3QuOjB|q*V&-lBt;{9DZ1o)GClFN zurbw{p&*up8A{fx1H(Iq59^Q?_NijR*rb|`O|rJUS{#8ZzmfgrV-(qT=gBM#HHbkZ zBUuEm;e&URk%3k_*W6CsCuC^N=u^i2?8LjvJY=Hy)l#+_vj-F(wv2GCKvA+^Ns^F0 zfQ)CMPv^KwXAZjwCgwybZ@uwxx}~~hLNK9BDx?%*RhOP*{yIcvi_Em=vhl#T&kuN-tNdxqhF~W@hw&X7o*Dba?cN@G0D+e31y`tn~NA zsl`;Qn56o0a&j@if}5Lc9X^eIpwbqX#ssES>}QalJU-)Frit4hiV&TssI5#=-@_Wg z!%;A=K!<&Ttp%Q|MuSP7i;Z z^e&XYYIwPnKi4dY-t#H{DZ3VwYZThUAFGpYcd=XK#mQoW))7SBHfWJtBH0b zMIa8c<0)a(8M2=&W(RGwVQ2%HStV?+IB1mRu3)n^I{{lfT0rIi`HC!b!&rQ%L>#{3 zt(LcIB7-CtmFJq1)(&?9hoNX;wwX{AiHiU(qF0io zh{Rde`kt~=i+{Inlk9JH=kOb`=b+6Hw9(0nraVi6*>qJ`g!tlUb1`FEzw0zt1Cs9w z3qKk`vdJy4AabC7imEWjTd59dOFHv$1Jn{EQInTSEe~^JCVrZn$X+4_B;0@ruFXKx9e14GD`hg&rPak zqZ}VGtF@E6!WKUB0_l2$MBVJ{RrB8R2vHq*yZ$U1-K4Wbr^ z569DP*iNEud!@NERUxTp! zg){Nf$Pi>~RI-yxATrussv>WQrW_7(B%ce!H@x{45o_~I7>isF8He-|rDx_Qa_&f) zCz{WXhKWtdb7Blng3zYyqVRf&PUr;g=IJpme>z#|#9sUehqZ@%89XD80?GE2++M9v zy|-(C??|>L8PuY|?G|2sP%l5pGcof`+1dIOV~}^z#15$%n@T?25*0qL3V%7#E9}jl z06W%5xEIT0WByLa`@1c10vv{PWPxmbduevDiao=eh!Pu=z^qd~0?&ImF+bB25aEhM zc{wM1#_e4uQ_L)zFbqJWpFpmj0-*4ycV-1Zm+^`bO(fT&q+%0L&36@3*uv0!s5J-G z#La;D(cMKIS!zq1s!Ve*23X!9^H@~GbV<@m%R~m|51t!TD$RNm9 zlQI5QQKBT^b>x~>md~*UNU1;Q&7Aqy29K-)ypd~+QwMH#&ZeytM$M?o%)Ki7 zSwkt8@9_)x+K{>RJbECDB4T_z@&c$P2LuZlSOcCPjn-u#i3(qA1LM7wM{mid@U9e= z8#GO407LE348MIhdjk0J!zY0soB;%V*f2eaEeOSysdyc4f26lK-FaI)R|NG>^!!o9 zbBo%&jOT4zcz$8yb7^N2^n8P!TSIX>3O^_5&ATQ!ehx^^3}3sE;K)+;D-Js3Q0@*xn{N7rOJ@Y+|>i^pm(`04{gaXf@oSSPxDV zA&tvp1>XDspz}*-*O1JyZivE?PyM<sez;aMg!h56rSLi7%|vVWeYe&P&7K&3r?J{GU2Z+ln>c*0H?!`MRh#S&Tti>yfKBwa!7-hKAOp z7jo*>_b`}VNWkpccXqU+<*`){>=uSjW|_I0Xjg-&^rSox|>7k^i<$p zFCaTa@(j-9-GiF`+iLF?+txW)m>BAwE;%9n>-B*o3ck8gqQj+6!NiL!vR z4Q&CeQ`xFcl{KkAfg~e@f-f9jC{V*D6wER1g3-sYBPW;o5?HadI=|EGd+l?jBv;ZZ z{4|2nPo)|p!3i#y67ksyNADgO*w85Ge5AFYgGyZ31P!W0-N5@NazS(yc|?3h%#Hwj z7W6?^=Daucv%KRmM8?e|2Ij1#D z{=Lv_MQc$Y&AgAIi`0g9^L3KPhPkR*^CG{7PA2#JuK5+<_*p69ztQ1bXj2T!5c%J$ z(e?AiQ@Kt==8^%W?l-t8PRR1F2(PlZL3b;@75oXEU068g2ae&Z0O5jIu+ZPXBb!Jk zVG-r<$M$O8MS7Koa|lEVkEigwY^q0q_>l}GfYla7@dg#29ShCQtE%)?88Ya{Ph22! zu|`N8697ZtA}5)<5F5aH8;hUmF-zL*I_jb*j>oA{YhUp&*8U~-h`2Hs9-x#ALY5*c z18>gYt#oc(e4i5)teiU4r3U&`puj(inZZr3@U)|x%~jA3{(VcW1<+(D3|mHfyNm@2 zKfQ6JJdPCh_pE?NE5%bElH~jtf1j*wSV9d=g~CGC6xq}v`P|oNbYzX~W2yyN6++4! z&Ll4=eb(l{T&@;ol(P%AD8fO(SS!MPKex-z>MLAnemv3j$gaNRm+)7>i_k>LM|t@2 zGpgCuh9%LSc`s_0V5Y54Hhds>6ITNYjuqhe=3m0mFqcRVf`bSGW%uHKQ6R`Sg@&a1 zzKO@q6J|CR5}mVs_Z3PysoDyOl8V|MUUL&kvK4Psw8%#N7BqE+XsX09% zV;>x@v=$1mP+wv&&tp>QAI8#K6(>z{d~of`=q0Gc&mI=_A0#Aq{yfsg|KZI+jLF1e z^#F@6N!=>5CciT=o@kUz`V+}ZFF^Ga5A@h4)Nq#$(E12$PSSz9yv)Ia-^(-?4@uV2 z_FYXgU)N&YNjsGc6$Qc$W@)>2=wvB$Jzro<8Wp zurlbpLPNeY&b%_mROT1j&vOup^)}FAW;v}j$BbuNddK@wqaXYkae6<2yLo_|_hGe2NfTcMb6SXTriI zLP-aHZ}Xr&69@J7hC#9FUtmyY0Rl3c^tyy6RJuwHem||YR+#mW>1tJYTXWO!E?e_A zH8c$-|D9@af#pg;IFK1-IS*uR%S1m1=;n5H^Xw_US1S$O+|}I8H)1!hZ0P3PJUo2N zZiKrZR5vdnkUN3rrOl21TWtJ=4UGeyAJTXucyxN>^J?%fl3h{RRBUe#xr#(Jf(bNP zuA-Wlzue{p)xf_eKu*jrc2P~` z9Bfkfb*|x2UD=dwo3Rd9AQw~o->mpct@tvQ1e%Z7J1l@0v~;9|HGjjc3gTE5?t$9Y z7L^xYjYeG-6|=N0>gRr4l;8d7qbw>F-<+^vwymalypg0#{!Cr zP}AD?h-DWuxWivWX26U8YS5DjY!aX!apO9(UwL|iY!d?(OL z2S6OXu}CgD0|^WK{%a}<{|qjB4g2gUy6!k3Es`Pd3NSYOni^Tc5g~AO5C~K({9zWD zI&EV_)^%oD0ed!>!TGc;*+%e0c5_BSI%O`8xVD=2vzB@s?nB)#g&&zx8GakIG^3yn zMV0KF4fh+#^g7XggdetqwD@o#8olZ z{Kruh`@gP;M`yz)FmWg#f<6gV;v~-j0jt;TYw>25joSS2+8hD!9HT@X+=Q~0T1u#` zqlfwg)$Q{f``lo(*-R5#U?zmNV4mxEitRC$!MLiQtPyf+^MJ8fRc-UU3KK(39Ne9U ze-`2C!qTK-FS8x4``6IGp*?Hdt`CR!{hN=wp`h^}k2|qHTMvzMugMdkfZWi5;RR-R zwVFjZ0JT4SbJ}17jr=3FgXw^AV~Ei?PZ;2p+F4G-p}!sZCF#XNe=+|OK~vcTads`X zErhXSquO>o_#3}o=)nfOa2KG7l{+}Xr_CETfn*kwP+u!9-zDcvNJ;R_J@eR9;9bqq z22j&JSYL!AiIJ{^LxBa7Lf6^0UkH;$L|QGDeG>tTlvxFRp+uHnWKn5gP3dYE87rx? zk0o0p#DVjUL2}R5^zwl?k{+!(9Yq}lYH?LmQHYKA7=M@X6`o|1d??oR&^S36Wer3) z%fp{z19D?|l$PIvH%LVIlcrE6wSs;B!HH2gkojp!nGvuA=qyLeoz03-g8(N-Di0X2 zTfcRP^FCbXq3wl??&mUm=I1Fmq}5pdCxX$(8sl+7kr)KF92h=)aDQDyMiGDjGBk%i zkBF%tETa~sOl$<{s63<@d}@q-C}JG)<^bbblmihOMKncQ_GEU;gV%{K7Kgl1pM~S{ z%e%5s+T`lFMcYQd)hg2Nq zJg_U#f~D+4{kOw zu=f6_=(cWgtTo}+HR*R%ga=#qV1&q^tPFOvR)%?zCXq@z-&}-py6A=lo?i2d#dps` z{?X*}C%A67h%tO#V|XqEGgK(H)d#0o@^9(I9eNQ@1IGJ~bvZbu_2dSbiZ1eO-aVvu$mi@t$g4@yBD@1odE#1B#YnAZ_=Zj{gIJiY2zxGN z0s@r0`yQlC9%BjNXN9BW6e)PqCSy!a;^r6=gNRvmovSvoHhBng^E+Pm zFlk)s>xwm~c**#Kb#7vRJUMzE<2Uj4c~0Bq{ImfU;SfbE3TeIt%#kIYp4vz}0B;U) zA)=oa^{p>GNz#YpD#L_vzda(y^EtyF^U4pUhA<~#5bJjaDn>}1>r@AR-Wx@R{i!e~ zWAf6lmz6Jy`}nFd1mxSimrZ5Y0d7teeqR;tq+EaO0aKUChbdf`X#*RG3|~-(i6=-b{3rH-T3RTHxhq2(C@Q|@c4{dbE2Sb0~&E&}BkVE(jXP_0P#L5f( zO5vT(KrKfG$1kLN>CR;d3Ghh|6g^vgFo%e+)3EZ$oJh2bb^52RI{hG>=KX`}BOVfwyRqHL3c8n zCFT{M?RJR;;8gtiB*S_8gT|${{QFe@?y}Ez7UmVZSeQHF@vJ=_|JI&k+mj6oE${tyJOtKf5xr7+n=?FCl*LE8KvgXV+1lekOQkAiABq?WD znq_~P9fxdp7i7EiCWDAfXymf0P^NH#r3UqJTi0DEDR=1V+4g4-k+qTM)o@ zB&#&4%wG|9Q&VN={_Pa2gTZb`1MMq9dZ*-TbJBE84reh3?D&s&JF$R*Dp6+nI=He| z)0!NX_;jA^aXO<@kkgcWk5AWn&`dj?92=PL4GfjdF*jvM{hkgn1$Iw!*i8=I zUS@xNnJzE$^aNkO^?EW{Rt>&~%k1aLVLGLlE+j~{Vn)tLK)x`A=e~D;8`FL%Jlt07 z-dEjf?1&L)Y0?JHW-g=l)bRN>V3G+n<6*KL?Bhv$*vCJJ`*;%(%GxBJ%9w5vF^#*_ zFWE3^lP*H>orlA{o-zx#`JF6x4SfCb%_~9>k9SsXzwoqA6)pLw-^y{jArv zj;mdbIGB{9J{{V3V4F@Sp=+T*)t^&BYf9&o_}X+Hi2>AEj;M%pptdd9_Y67@52>9wUh2q02^)#cBOwI97NvGNhBba0Wxb4Jxu1!XecO zWj8@d5H~eNSBr)U4s%|%GYd=DZY8BxoLMu;>1c@6_|K#o+Q?D4B$m=6%tZE_a6=h0 zkoQtV$LoSmM7<75>N*=S5CpDH(nZPO)2VTLay%HoWDd;68_1-_TAzqSl#}{1rY0&( z2D76H!3_-#rv~p)7<|kjm-yBbJ}sw8d!@|`AtTHTAHnr61diSdrf_B|Hyk(H*y{c(2|cO zZ?_ojueo+7bM9V&<&%a7lG?&oU9ZxhtApzInmoKoKK3q?A>e> zLXv4F_H?m7wSqEcl**3hvbKLs^LseqDJQEKm(+Ae*k#Q5obb^RN*`|aKVZ_Cowkpf zXjTC)0EkD;p@=dIH+v6)t`j>=G`_C@pI~RZHhlhOiOs|;y&0v4?S=za?F=x|cHLv3 zPt60IEzNeqbVEXlAH|XPlKb|wd`3340%UvGqEctHKl`lPk9oIogf2sV*tKC5s@OC? zFk+gJ1tkcPqRfg+N9to3>_Gk1B6+=gV?ClF7`IoM=#cqHL%)+%FMQM>v)0D-{ub{) zZSPlm_%S`NA#tKAlBx~~Iqr&`2>^FXz};dF0yIsu2b{+&AlJ@ej+R?PTgynsI@9g6 zF-6C;#_Rb_|6rMm)lk8YU=}k!(Iz zAC1%pY=N5$iMY1MXHy$Kx8iH`f{TC?RYux@gA z7fLKkE42$u%dIBX5!%B{wj$^%>bjMW^CC~H3bH~76l?-IzmLcTi5zL{|Hq2R3B;j; zYIp_RxK8fZ*;?$@>e_csmPt6d&K;xS7(+OpaM!k{bg#76U2 zZSt1#C;4Ucv3faLaW6~5;5;Dnligmc{=62$faKGjt@?{vuDWu;uss&~_V_;3s(!(_ zdtQTxJN}g|R}=56CN=sWt>){fO2aCsL(?`$L~haK^ht zxP|i&dCZUz>1B>%-QG@U61e`HM);jfR8L*4j(SO zw3%xJiU?g1niiPn|G{(Bpo2T(oYU^XF{HL5Mr}_R_1TC~b}ge!IWR4A&q@?DapH_F zSyR5lJ~<O3d_fT(z}X-#&&>Wz5?4FK}{|pG)h=m;oQv^&uGx%t_nIv{2X%aN>@z z9iZRWJhKy8=O3)M9psI+1563YcEDG&9oS&Zfm@ggFe@}$0!r+Wd>;xx61kQbsniZJ znZcMz%)KWvW7#P5r6Tz{z^CL1GwDrDmDSWeiGaI*A}3=!MU#iq?m4x(;cwW|Cbv40hHq}bhs@9u83ulDArJ1dkmY0#=pEyj(yUhZ5($gkjXY1W@U=mEG0tzcBx);y9dVb zV}i&v`q%SoA{CCD-y@6S=+LBb)_ZO>OMgEhvh8$H2Ynxa53vsLMiR;kER6hHx zu>nQ_R%{Od&>KJdvKzYvQ=uPrF{PxFF$E0zo)kV_Xa}|0$%GxB?ShcYf!0hc18 z=2pY|QE6Skun;xaIHwR4qnEb1>_l{Anx!F;$w(dQocl#p;rVf-?f)!?)6>OV)>? zh+b>}3#Jrtv5hBmUeAhOJGKQsEP3jhW0#Fqy7UKta^dkh? z#--w;{Hx1%9%$FBzYh}tKM&i>}V}~j-X+Y2f@w>8-zSaY)nXi+NzNxiz_WPrv|wApoqFLtH>ifaWvj2YGA9b7cYfke*mHB3ZK)-Z;tyg z*M(dS)ek*E!j#&tRCD#*Lmnp+rzB^u2;XQuk9uf)yY+f}HA2HFNIk+__EdoN?1_#r z+L3Q*-AS=raYw#|(JS}Gu^}4^zm38&poyLMr?|x^mFuVTR_o>7*1gmhR1Yoi@KteQ z+6i~IZl#!5+6f=AK3{sA(s5=FP#8)C6tJ<5+DH9JkJ)^EK{efCO7AbG1cd>`U?uMP zlw#6E2tIiNWxpEBt{)*2${z%>|C9D&0~a$T;#FW=lHpc_kKTBEgaFT#ac**WoASMl zAmxCvN!t+hUlBguy6GCow1IwuhS@Q+ku;M)9vr1fW(F@2HyEj)G$@O<4ZlQ(QUGOZ z_0P(PPZWaFVH{C5{1L!uB^!!z5clV$jv+qg;rsNdapIdKDZe5l9jdEi&pk~eT4O{A zt_<}MoxpM!zx%KJiFV>bEy57=5;jYVeTJv(>yHb%#-URPaHU7bKRCS0fwy3(p` z+q--FuHA=gx78d2NR8oJ&B0fg8&Brin`1Y6jEJtW4s!S5A1vT(cRnj?Me$_v3R32)oO7Y7d2+|9;N;WaHTBgHWUu_>^r!v zfl{2vdN#C`uDwYQTW1YI`WOloezL#nF|*GRaSUS;Y#+VCk~`Z09QDrMb*(GxM3J z)P#vcC7MGRAne|bPXU%9Sj4Z2LyFKGJOgBHqVzt%<7=5fSB&Lum_UzDz{0wKhRv!r z0TY#xtsEGhYS9dh#0i*OL>49sMOdHK=mB1Yic%++!&_u2=(O~g5;4P{-=dQ?^Eu36 z;8Zx_LR#xb+z1D4oi-}e-^#uh>)|99Pl6vI3oM4@9i0xwXlVwHmHpWyNn0DO-9N9j zYe_LiG-^3v=7%?GT%`L}PvMK0FwqOvzQ0>Qq*bu5cfjPqNv# z^9AYuz%WKOP0sqr1;H{2Xe!6yEVn`JKVok7JVW3Lk2E{a-glO{QpG&KDA=Rs>;zj45t(W#@vl zP=tc;mb3^&e&v1cKjQ3>64n#rgh{a+ZHH&48iYt#4YuFJd{)*`VV6~G)SOW=#1U$^ z&ODEZPeo4q{MeyVfCqH%(Z=d&TAyp5)-f4X-?TI&r6kmC=gW0z|tB1N$@gCp=+BKDs63Sl@| zthjOCp=~?2@7!BE>XUjp)fG*Y#OM@SpW!rg1qwAn0fA6Oq@n=ZaRga|WYx$T1gjux z5UiRmAdTdRlmfmS+P>>Z(h>NXs2Yk&F=Yy(tQ52lV$d9uvk9VhR|h%Xmm8axJrki2 zX=!pl5;!);Eq3Qz$X9t3H8O;9gU(k-9dZFu{2d6J$Z?RIm_qgnntVu2o==I20s!Sa zt53o5CB1zCL^H!8Tc{QET2XeZY{8(NZ&a>&lbuC{*EtQop0@v8wSUXR8xGWCCIy_3fvp6A7^iP$|qy!BRQ*lE$D60b@7)g)e*ER;p(hTF;C z&^}SL|3X?dyXWt!RmGAn2xZNV&+GW?Ru)3ZcVQv33)>nVEiz@m?)Ij@lO^RZ2s~`8 zE!nN9wmoXB{S>cFFDFxbo7Y}&a<#9MY820CDMFQ?$G&-$N@bZYYU1RHSmDI5SFa}a zx1T1sU7g_ek+_<-U)|ZM`A=^e|FL05&=sO-mRr?nO;yJPI0bz#;AW$fsjKt|3lv0f z5+K(ZotP%tK-Qo4_I7Tt{q+K1*df^q(dLPkK^C6Epz zkbX(1Sh2C~X&&3L!6_vYWSfm_lgG(?*Vb*lt=mSL)Vh7NjfLA(>1$XxJLx}S z;X1W&Lx8wnlSIsP<8im%4mZ7j$FVoSX>f% z#S93ibAGdmwFyxkZsy5wz=+ju9{6ob&h-IU|EWsnCbb^keqglwAx8-2%Kc}Rl2ASt zS$9M$BV@O6J7C^QzCU^QQN2litf#(zo&@_wz}L^_l-((52@4E5B}A}R3f23Rno3go zN#39u)z`7+xE!N)(a_v@IiJ!J6f+iz)|BJ)cocP|5jX1gO~dw1eYAF{w4VK#cR*X4 zNRPW?5RA& z(4O$MG;2?5;SOC-gFAfEj{8d;@zX*i8AvLn3@kpX%%A!|gnU+V=%yd^z&kyAxJw4qu zGo|V2Zgo`;hZ60l>ZpcP+qN;UI+^&RuV421+)nlV6aM7yAU>l zVb}!11%!YCh`?SjiGWo$Ou}r8Si8`!^L^*s^WJ^0s=BLx&{f-hTPmGB#(+|7*{L8{m_GYlyMs=m&H8cVO+8M=iCs%)Zw4IceFl0`0i16 zO;O~RA9(mk3o(-Acf@TSZ$HX%iwqM4>+|YWjF^Cj|07Myj-K#}9-hZwl@ygej2p+| znv~~OFV+G3ciqm8hccIr{Cnj8#Xr0xM-{k-u?7-jJ(nXa-Wno{bAa^wRd)IM-xP2CX4G<1&HkO&*C_dfV* z6EdRj#dYT?cveHqyr73Z=rKeHKgA5h>O&;Aj~$fO?mP0UAS-oV(9Fl3 zXknP>o$rAO>&^oY&g!#-6p5)Bf8fF2cxVR9UXZ%nzWKN{7F}>^jDm zyfOZW&Sv*yz#$J6ALbQ@Tjar$^79n#yuy-}SZB&9gb3w=Sa7lD;MuYrn^5lk;8Qa9 z3qZ$#ras<*!Cc~8LEA^Zkb!?Rz6WssM>ry%!ib3e=|8wlidvk>BP)b15xfttNr(+M z$MKOi#_@M}CqXUwpu=Mt%@xA+Y7d4hmFjj-X_U$xY9TvY;n}GGx>T&C{YKD(*+)0* zLZ+yO)NTgrAr5tVVF%_tcY`qO!%X60*b9m)g)8Cm;(RWg%iYS)=8&}7uhn~SPt>S3 zy479~HMp?_KSj;;jQrPcc5Ca+Y9)huw35P7E;kp>FFuK-yVq+q!_L`&aF$lPt$M## z4Y`O1oql7rUJbfiwO)Ax53RRC^)uVW!|?51tE0^k%5ar(L9MwSur4VUu0iu3YFWRE zTG#a}hL2!{;nH%hSbQqHy0S1^%r7p4VQ_6JzbN4_KQ-%x5IyNvsh1@QUGQ%Vw}Lvs?_9 zW|s5l!NTG~&aW=NaBF5hKW7a-SC4*m+~A&_0;~l=^Rggswp6dLmdba65ZdXP|IkpU2ACp{oA?uPQRE4FlXbLqxL&g*euDWB+(7{Wkgw4wB|11j@K?fPP&7|~TZ zsMXt@AUh@SblcTx1pvk4bZu$&MxvcT*n)PeUKcYbP)Mm!?FQYAR;QOq5UGq{t=aYK zF60-*_$XZKH_N>m2#ph^->r5a=9YR@1(Jo3;NwU8x(P!$6yotg9XkI`w_0&6ll+?1 zey>xiW1@1H6VqU5ZH-d1zg8-faymf;gA{gZ>l^Isgb(MItiK($TH^(N@a; zC4_Fc)v5IaWi&sF&=cxmnwv%Er!iDvSgMv*Yhk}$sckk|l^}ao>cEKCdUum!iKiIW zYU}+@NvJ8F48o#YWYOfjxS98Jyl-q5k6ZRO(f$>$Lv3hk4;E>SZkFJIDIJ_lMZT7k#6V8NHHxYJi zkgG;T$+q7H%Dh#i+`enePovdG^_3WJ~gPYErT8<-cw6MN-gr=?wJ%*GZo7}D3La>0bs5UEX=*+3ie6MmQ*eG>%r6jV0 zYYQux+0TA9dm*E?wVA2Osp)Js`&j05qulO4UIxI`?bD6PcD>(4TyF!s7T;_2R;hOy zMDuvJwbnyKsZ-u~9EP2n=ewsHnbYf;(~J1S)P8)u*_Q@RuQgkztJ~#jo1}xv816T> zu+E?b2z?!tELwS+gmAx(j-6m0*sRqA(Yma~_IFf}1|hI#n40 zk8T}u*}sTt!|FEJ5)fyLgbaqTUL`c)H?2;iG>mK3OU?CuX}xOWRzK@At*cx|W6rbm zZ)1o_3R zx>D%G^a(^R6d*t?uZF!&wVH1fdeiMvt&_je;3`?@WhcWzwT@xPc6<54bcm?wI@BQf zMy^njh#a!q1W;IPdSQO~1~M%aX2NVXoT+1mhRs%`n#-}O3)W1C=?l)%>PQ7kNQxkk z?4pk=+n``L40*sN}GQEZhJ zE6GLyCcry~mT#5nU?5nt!W&gczXugbu-*eCaIIEbnSBOKZtT7i^;5=chprsMVAs`Sz1j>ob8gm} zx9x{p7l!fL@#p}IpX=TrXTaR&_>D({VC9R2i|ChIa4DvOq1k%qP<*hpaO#;n{H*Vd@R;izK&Ut86E$<5Mw zt*q!DHn6@Vqig*>c)L_T7Q2{MaR`K4$`?aupvET)U50mNjOSUUi1flfsBTgzH<0c1 z09vs6lpEN=j1R?m72OB}`i9pcDX<7%Wy*}8*F!lzKa|BZ)zr!&a=TE!E z4G>mHVR%WIHOVK+b#j*Jn6t3pI@KFeQ>od@ugD=y;HqQAcsow6pjSDyhtfi|B?hV`<1soC1^uy<1yWf zSazX8mc@VW<_xW{6CX<(gVj^fb<$};PAT6?G}wN z?qVYaO=k^r1X6E)#9ZKhrc{U*&4Y64^uAyR83GfOfXe=9fYv#gAd%=~f-brY1t-aY znp*VjVEjY(MKd8L^kX3u9z-z-4T>vcjnc~0WVGCZOp&7jjTd$l(N0zuhb~ma)$I9V zD?8;nG#+_nvpx?Kk!MGIkhxF*zn-59mvc*t%f+zx)KV_Ajr<{CMi$yeYqJ_wpf-UR zHWPx7E{qx;?4vL(o7HZ=yrBa#Ue9#^u!P@m`{HqDu&dL_uIt!A=aIK;)Ee!29s;V&S{>e5TPqJXS%KG^A?3FOvL|E0 z(;kJQ+?+hDrD<*o$#yF)Xc#;33NzIUB`T zuo$R^gDW#DnBg-;Ckl?Si)puWH5KOyy&8|Wz*oJ^{`s_SA-&f>4*;NpP+BHO9->Y#&ms~vD-j845%V1lXs5hxCO8=ck`7Ws-QbdX?~IZOx9 zBFQ-*7B|~A$)XU+vnM}&DAJfmreo@)DBl9@-K|m^D-;AH?2FwZzE&;q_|R5y-QZNQ zo3~%s&fg4q1B%~H_k7T9oKktz&_1}+cNzAviL|OCRr&#j)73RX8R1eRUGf1M%3vSk z>G1ce=YUXv6+Fd4FH?7mG*P;k@C-4mMDY8@~Y7$eLU zm*-`zEaZw|;d*}QCQd;DEO)p0pHq4raN(AHD1d|Fa`YBYl*yx)fZY}y;i=BBM^5g< zQkIh&Hn&jxfK{y4Y4l;r#Lw)%tpv3!f$=?v_(5g_`~jImjmnW=T0FqZK; zoINWxZgvFeB%JvC9NVtV8f@^uuwkh;qqgvYYEPi-q6pTmEu&dWvN82xc1J3ci3-Y=e8_OVHvRwuXbZlH zaEDegjvCimoh{k{I9o#4)oft@N(wnR7fQ`dC|IW^6`za!9xa;st5j-Y%YJb6O;W)L zT8_AsZlYwTwTC(D9Xa_*x1WI=7s(_k@;RWOo{@v(LqIII+MLdWNB4pUU7o;sxdb>O zGYb$=rZ;#_M}w1+S)##No_YnpOB3NKwP5MHwEWP|A$PBA#P%j8Ne_)CfM=hI8k`=tV@aL z8n%VAiwi~EjF`_YhlL`wk*eW>0Wi!=s~zl+7cfKg^z^{fHs(8NRq|p%HOa948Rt9( zQZ^`)M|HD{E7;RQjH|V4JQ^Uij-|k*o-X`ibL*KU*oO8xy%vu7uFS9Gii?ZI>$s7S zU&i^%7glmG;+Gka9ieR4ZCjFyRi>)%*aI`wEe$kZCTGn?roz-925J+_jj-O85i83L z9OQ)VdMeLPXl>uZ{DG9ovoD_cutanKq}>}A?KHbt##vCk7qogCVzP`)xYjWq5t<`mm@0a!Og|F94|iIEN$-v6=RyRIAQW<#&2ZLe{n~h1uo5yyW(=y;_PJL zhqA#}A`zc`_jKF49>H7yPN|qyx%GVY4;oj*N3GF904D=5OFn!Kgqapi=3ysXi?Ce6A^2u=`c!R_ zAytB6{w72c&cI3Uch>to<^4X({Z<+0a-Ez8@rukm0MbbxKl#l)d~r&GEs_en6W>%EQ5U@EJc zQ`y44P+Iw1XjI7Lx;6aP~r_+$}gQ_cDz^J^tqibl6R+1nRtahus7r2+kHv`;=6XyTq^2`zzFzV;T#8D|A+Bu6@N>%V@(mM4x#qI7@ zpcrvpKnuxPm}qB1{L^a^m^29CV2F4SOa@#zmgZ-k!ooCa6=i05-8Dh1pZgQuM_40Jyf z>0rLE*S}r%7_WmoSoK^ZE~v4ku_yR+*81~6^KnKhLINe*)ey~Ui1uWgOGMxi>ezSv*e=@3a3r?z!&@?q2{Cb!Am z#3d(6frumX)nNjVPsX`;7s;y@W|Huw-#mogRK{cHQ2P(4 zYPru1UW8(YUg(0WxMo?QHA42HN?Ev=i6iP|$0@z*ro_g`z4%@@nOT94-H+^~UIQW{ z*QIXkqP`Xb9(pbf`hj^O@b{q5VR?Ff5PA!tM$#@)iwI=#8TuvFCFs`UW&jS2no$W59L$wZl2cf=HS;a#86Ahb>_GIG1v7>qQ5a1O5VroJ5|=sC3w%<(@fJ z^7UBPTbIZfw;wyB(-CpJZmnv;Yq+&6JE5p|w$nII{ zsv4zdTAf?YRhTfAU4fM)(;42GT{p#j;l94wVja$Bnr1>+!0|PVCBWVCVpw?yrvmXF zRkmUS?H>Z4oT3G3IY}hyx+-pD<2dJdqSQIrg-6o(Fb=kx3?gHP+ZA@DoQ&HOSNC#4Y`4)T z{q2%C^3k1Ow23;{#_%w>8*e|P*T~oeWmgVii{uandBA&HpLrt4UQiAp3#!`!LWy-d zz<*EizY6qdVmZ^P;$9iNW74$dFt9TEuq3gC*_9?t7YYd=nqm85Y#b&SbD8&-La+KP zbR&CkZ8ls4*=fw7Ds!rr_AKJ6)#Fa z#y=G7=w&=b&a+(}BZ+M*JT}wBRi+A{8IO%#C`Xqor1TxF1mvTRaiC&VCx%h)cUuVJ zVE{fLBdSvoQ=I}!BZR=j9yfRP!dRS_lxO$)4n}h{*b-rK9&qG1q7%CzXD|J7 z>)HUt0m0}sX(>|tMH+UbOCc&|C zI<3bWYGttv5+D7Cpn~KTm$7$iV*LcyS*o%;;E2V7jU4uf%L-8%YrW%5vuSsrVSC8W zel{pB=dWFZmBE#jVsUYy5E+ZCKuqQ6@c92N)A}mNGdrpOK9Kblyn_kx4NuuiYqHT=nHO8{vPhacDuFo!O?~k;NS&z#d zp=>6@61yF7-(&ak#flYXzBKu9I}313s&Z>(NIdQuzq(GRB2@OTDlB3c$IVF>WhOpB z*m8tCWJ)J^XXKF$tONh}pJFL_X_Rwc+p@@agLgMTFXQZFLv|=l zLtPPeRzqBsgnM7_Anf$v+=+75Y$LB}AA(3`CvE10bZk<_S~=>{qfUxlPpoFA_Uv@m zUTT<=3-hYM1|2~$2ZnJdj>|5ZMbYTqJ=^D|Fdp|qp`_SvHg=u*qz6mPD-jKF(hM=|c6_R*^ z>vog})Mcfuh=!fh@ArJi43wfx$^mqWe~oqlDIY|!c;n744>tJGnWl36oy3*IDx34= z!gHaxGLr>TG>b!4PdnK;IMN5?{sUAAqap8-^fIpD-f8012zZ;ZU)ksvUVL$>XQN-! z(XaE-uM5$yi%`tQZi+?eii;RCbK-b5yqc$m=n(cPV)`Dv1fV9Ns!!Q$!AY|D2G*!f z6$S!Tnnvi6i*YY*Fq}$1I_k+(u%E}{DYAr(C+SDJHF^F@JQ?XliNoMj=^Iu%tb&4-uFC4BQg09(RMhcSr{Mxt!f>gukSi!|D5pf`W6r0&ntQ++HsMz9FmRbRsnJ{T z2bc52^P`#sY~2#$3m&S+=MCeL5tOWpjbPw*6IS$gw}(aW5Vq8Vo?d)=vcz4c^ue&4 z3A;htuO{R%nU2(;^Hh)an>^c#4)c{^klBJMh@pE?ls;yW~eb8`YZL z9ag=dYZg$5U!I20JjF#*TWD!g1LM_UMS&%kGw(%zfTw> zkdxhiykwr$XT^kNzSg|sCZJwta!LqYHY3%<0aj5b4ed)`PZZNylkZ3!0uecv!C1hx*aEP^u~Mw_DqXmv$VhEwfmS8!X~8;=QMLX1?ufM1voHl7-J`}oM*dc>JU zpuVBpCPL4Hb?gsQurgtLfl3o~YWg#f?}i1KE#-^(TX?(k zFi0i33<9c`L+ZfY!T$mu;p8=}IX+s3XAsXD7jzuZGx^3Vi#PC`q|6p?;6?oAMX{s> z5e$el=MX(!l3}T~Kon*G4mxZYPIjIRoBc-gmf|6-Lc;}*g9=?LC_zC2T`*jK)?oH< z2;5}U%m$|D&1ENtkh-n7DUZuWzTBbvq~tQ*^hcNEsT@ZkS7sJ&zzHUNfQowD4eD6u zf}=PAy(ET_A@N>d^R|sRf08>|-Qb))r?m@R>I+a%mr=SA=l|*>WF>xXYcI+Y48utaS^q9bK8h zDQtk1UyZoX4Q?d%2DIsMKS1DE^!buQxW?yhf7awabQ?vx&ckRKqX?JccsfS$1Ih0; zY)@dA5lPAWX6L8$Vqj*!Y9riUiS8+k?>2hrXgtcOQ89gZm$4t3yt|ywRWtFyMJ19+hFo;R&rCEKfFR z5LIoui^&I5F3M4qXP_TnQygJ3tG5(~Xb&UVX6H-rpni~ki2{_YvfD{ZDqWLjoXYQt zqIB&29FT*lH{6mCe9$~kaYN(TiAL8b(sNQ7TEtrR&EM2Xr#yS%Krkm*w_W(D0awBK z1^CnsaF5(Or5;Zxv7;zH;dcnapgl=xt|GWm@=yPH0370RiFMZ%XWc!FkawE~qZBT% zq;|@d!&pI6PazFXXe?;nyW*VJX;v5Co#9lRg|C#uxzQCJ!5%_Cbs2)VElxV=emTVT zrrIW-QEznZMfrq=Ib7?s8qRHz^0I`!bA*hFyJq?j%_{G4LH*b2suK}kSw~3=<|Nk- z)BDgAO>Vq-9NX}!a#NREowbLz^m9_}T^b#@`RU!i1VdN0H(P4+NfBjwyKDcZhIGO%x7({Wf~`EBffXlv;IHHHq4++N(o*_m8FfGg5t0BDVBDau z%g}~Oy&vHE@KPS`FK2Ms#H4W%HV?xHbzl$T*>33o9Bg5gt5(T7-GbYF$VYn~&>h!6 zt!*6lrlMPpOobrJ@_`ciNyA-D_!v_UX{auq4QH{v%x!~AaMOqH*O9QIw^S25uQV?> zu;$#qbGRulTXN?GcEk&DbZEL=Feab-jL&8)b4#k0t0^%mJ{UNSt8o^w_)4@v#Poan7>59Lwt$ z&@NCfA4fQK;ds{BF*^V=boSRn)gwvz!0R9Te<9gyQr+9b36+2JYUBtZDt3h86p#mF z1MxW`?t(lP(UV)`Y3~{NUsxRhQ2|SYDyBhG&3XK00niZkks5 zjmBN&sRu&`HX})QWuxes%B-e7#v)d6z6fhRG}B8L#yKt8$R^z}89F-GKI~^pl)6`n zosi1&cIe7S9I^LscO()^?85nJ#}~&Ny>n(4a5&c$5-%ibyN3_hLD7ff&PwT8P%;Y}LH%l@44jQ4yzZ+u$D>gS4f^Q7FV28Fm#N zjX=v$-Ars%B@KrxO}K+{094>|91mkkhYIt#C*_UQ@ahcw@N=L$?V#KS<+1ClxF|Zw zCc<*BQx7+`py`K`pK=@*l*;9PLtB>~N15gQpemj1t!pyr3rCFUh70qHPY%T{k&FCY z2Zb~)_?2$cc}KXx9US~BL2x&rox{<`e>L3Aim?Qr#e#_mUhwEnJkzS)ojB93m0PE~ zl{*vsn>sUfW^$t2DNl6TQ#{&ew3-ue?NY&GPt}m^PVm1oWv$eWgWyIFn01Wdq;0cR z@4=qH`G}Q6Oy#3>u>0O6z=wE&;mqEJ=bBN7WJ z0rqhbPEZ>aRwOEd!JR@^Q)yNLDgxmQ0T{egs@O48P-%6b$?l11N<zzQ%r z!7;5hIGPp=wJn@w%hb{-L>ldqMl0M{NtMIu5P*`Xv%xhm;M~CmHNi`ww_IES01{1S zUXAiuz6rz_X==~qdR=t#m5cp%e2P)Q8ico7zq?W2eK8gMABhq&QPj>=8- zsL3iB61-B_hZ_Sh8_6K^ojfQpHf}7)Rm=}m5MIHKcs~fWfdW&};yT1=IR?R6u`v*| z(fmn)OwLCPObokp-_Rw5)xD_6M#9IQx4Ga`C2 zW(LaL&QQKT4Zbe`v5spCE1B7BHj|wOf6iv6Ca0#e+3aJP(+wSl z5!d5mQ}|x1w@SU!5TPFLw$^%xpnv4YOP$8%`R?gP=Ja~z^dkOnDEs(&vybXeui5CH z#^aWBD}d_ODsVd-h2iL{TE3%G0Gra1>#IYQ>$s5*W82u&cY`jb02m0|I63A^Wa#-Q zZfcTYbsP%-{v)grr>QZLyck{^D~!xg^j(x?6ZewK8?`pyE5&*?IX#JU3U~lIwmlmImdUJ}g1Egaub_AJ)3^0*ovGoqijO z2xn{PL5#V#M#M$;*deZu6ofPW^_)=P_XEj)53^35)Zp~ zo8<}q2Q0+R3wJz7m2t@3cwK|$jU9*d~GP`lcNWCp>z%Y-LSCW zQW_bPaUzpjNu@f2q(1VV&7RP#E&P^K57Agpt=q=dTsR_OP=<&L8+`hTG5_6G|%K$3{Ae?zjA4CRG zRqf!_n4rexP^nt*>#|q3%UIgD1+=jc|CP^#+4Dm>p+;WPl?Y~=F@z_I)S01>HSw}= zSZjCS1*_I6YsVr;t$WfFu7>~=GB`JKxSEUq&kh5_$W7d+1?7H6bht5L!9^v`TWDJ1 z2u(niHmsYVT~Zqe4b5%BL>m&c2&b!cZpXJE-WzPmUSX<(79e078rke18nr{UwY7au zdW`4<{xBD#VRK^*-rcHW$aIvz>EQp@xxSJaB*)eQ9~L@5=MY3Ft8MjZM8Y^bg-I0g zn24~F-NnpB1a|8n7;Yjs@iFUl%FC&itSLcFp2gx3mm_W#ASW<{lOtz|2mRqT0b1AZyL|`w_)LOw;+*}?)B}4#bVi}A?7%=DQScBnKArU2n z^G3xn7SV4f70YrHtYS3{t7{Npu!CKLe_{yDU=oy}#$nI{EjId)JJFi(y7SX$OJZQ5 zcNb)dDXmL75YYo4hEX+Q*cdn@o{`52e;1g+8V}^ku$t_k<*BZ&}zE=wJDu57!_GwmMEjG|Wgaxhx^ z!b*NQH|Hnh7jDhW=jYtBs@ONw%#P2n<&WIbvLV^oaECb(@6dlxaS^3yTX>ivjgbZ;^E2p%?0m+Fegty>{ZqvP!s%*%Yslm=* z5C&;AX0L8F@o+7(0UfWbuD44dDPq7WX%YzcigM^%Ge z(%GV;#~sHoaM^+ZF+4{+Xkgqz@#!8X<{&hY8?~{-|6xbV0;4eb4!HbKh?N(Es3^&@ zEy%5SI6J?X4VQ0ECPLXn2(DtVD-_nU3MuN2CerP>y#DKoMqyVt3Qwi-pT*e{oD|?G z;fkin9ggMO+KMqf6`I0&xufT}0mNVUa)tSXUNeRgNzYQH7qEkdH5M+5u;#U>W_diT z=yh%~EZ&Yg$B={0<#b&of}noE|6IzW-pk>M>5CK|mUEyHlkVr|@oKTxx8iLVPp68O zT^uZ0ukadT{4EFl0AsHoQ})iWbHs}RT-1m*gjuzMG`7C2H;Tr&$|&^{3p z053Q(!*M^3TD35fThaBc_I_T9Bq9_)*R?kvsB6+*pTEjg-dF5${bVYawD?V(KS|Vk)8~-~vkFjD84`3wh3D9eqc^u-%cvTU{@r(ES~9DTL5cm?NfoXX|{fYp^M zWj7$o0o~AIMvxzBlIu}KPr#7)MEB>X^)4DGVLz^OPu|puWa5L{crE&DDVtzR3>l#j zwj$_UyXJMl*dCLi$HqcTrq6jLDN5f81~!J|}uUr7bzMQVh@;iTn@Spm$>-@sFMoPt^z*t$}$m^AZ5vHEOG zc|DvhcCkx`K|rUz2H1E^OMsxl;(VyCX#qI~fyEMyRONyiWOS*Cd;ax$tykXQ-E?R? zx7jN!^R|MMT4cjnZDxZ_993^TM+XeJ#{t#DT?igsC@pZ%U)#*uk13p?qjd+3oTtl% ziHrSOr&Pw#N-QKnOTiOlhzwepbivLZ$geSOF3yp!#t{^X^6k2_o721z++qUZT8q4* zNuR?z=z@cJO6p);f(`Tnf}>(l(I70y5C9eKwv?6}FwN`rp{4#J~aUrpQm3Cr;t$Xtr5v>!E}`kCf6Nu2;q#;lGAQ&L8-f6ss$y% za-|M>g<%kP4WJwu?5z?wOQp50$d~watX#^WAo+A*D^tuv|Ew%8kwNWhtTJ#+{YjBY zi3hhluf$lMX3qHX<6$I)Ss{y#u6>j*fFCW99AIy zic(Rgh}`QsizX=7!!;Zz^X9H!9m`lPV1GEKAC1)v5fKNy+i2MO7M_By%wOL;TGolwmQ5gKe zdaevZ1UF^6H3&7ZiXT~0m?RoYn6RrRgk>EpUM~{uI;%aEPPn&_DGb46%}TOcZF*>i z-JAeMsf6#4V4N$swa~$9bUctyD7icW7o5!+k>t2ym6&9?QMDs`NLArov75Y9at@241 zAW8{_XKABKMuuM8BZCoZSyThY8534WoAnMP7C{h<(>a2$U49lhU_$;+53NQjE{2RkG>p-> zYXKQ{kj4Io$cJfgfDYQrq2qxU2dcW^qrvl5r_`41W{c~mq;S(LAxUc_Ld>9vk~K<{ zP@Guk;dab?twGBdG5@quQYE%P`=d5(ZbHBI$%|<9O~P{D5k=XB@(fODGGF>lTr_N! zp4r!&C~t401+h7p;IR)58sM(#Ou0=Lf0dHHyTWe<@eE6Rk5Y3=>w1uk>z&vs@EC&z z1K2gz;adj3;6Sw=p2tmTXk92NPs7a8eqPj{Uerb0gLSD^#-gT?dyJ;evIfjTcXh|T ztX{**&mu5J)t_@M@q3JFk!?RDj+L%iq5SE=F)=KXGT|6tc&M5>KVAuktJGt6g*yy8yBX zx9&~#YtIGYTCYV@ZWq_>!&Q_8%8fSNCA%Q+C`W+`)6lMEg}509)F@ADjBYl&oc-FA zmy=b(EE~Ewm{P_SOc5|B)xtFj?0BA}ra_2Q(8W;u#e2MxgZ5Ed8!arakmNc|#E01L zuxyjQHVi;*Ran<%w+SU#v$qE0aXYZ*qeFK~n2I7goAG?rZh59fllj+4dj-Iy#t4b+=&khw;G?#vKKS zxiR=S72&TIu64olLP!kYdC+UXTwIV%EJ4Kg^#p(!m0V$}bFZG~M7iT`zX;Kbe7UD<%EzE3TJ5`1ZCUF@vpU?UiFZ+1BZ`1XVbF!k0O%tQ4Y0yO`30x` zP|9s>i2a`SoF`^#`vA*njt~6VceM(kAymaH@gXb~ArrD@ToS}pBU)LJ`=RkNz}23X z6=g`owdYN2CefUo@i-7fUq^}r(okI30I=Ux4;Uag?%?^{0me-s1ie9N={Ow*iJXpS zDjYD-ZpSR?G`HLxgr-98Nki4+DnU2NSFumq1nJ7Lt{tS%I1?aa7hm4MQ!`4S%nVH| zH1Y8hBO+ZUF%mc^i-fg9@9Bz4|J`wg>!>S4&cNkL<{#)- zYs9EZjqHufziG=!H15oyqiHq@4o-}h8_HyY(gBD>s^V@^CNN=TU0v8g>xmoUlSo#- z@9FGvI;WA^3)k#0d_^PgREOXr0)<{jkagRqFU)q_mF3=-N{r67k4Gw}2=2bvo`TC- zb#$eHXo#h?2p>Ujn@&BXZ3l@PLnJhD5RFxgmz&}`mEWzYu;4^-JmX0jj8geyURFKs zL`5n6**NkksvIZVV^PcJ@Hi9^NxG6L;(;urDpL@V3`kBR)-l>a z;o^np=Y{b)aDlIB0(H}4W_OqdnhoO@&+1QtI7UyHeVl9;0`!sFua?|J^pMG;6`e~% zc%>kgIdIr%hrwjz=%RI%m|@re2h~^-@Yhj#jE0ir6pfgy&;-Sis)Qpu6OZz+`pB{@ zWQrbi0mqAp*9vECfsFI&zp6&IJiT4%-V z;|{I+sqBa8J~jZXjtJ2833m#385v@(8%dn53=U3jFbZ{wsyl*$)+NigFe*Mq1!CEH zd0-V5uL=}|$)NB?!9}Tpz1p5sG|uIDohiIIQ@G((1)D1p1ukGja1@_f%7wG@Gq@)d zV-7PPhosn*Re=H}0?-7#tHlMy=5e8zTfREu$ew75&T-})aUmvp;~iUcYm>NkLf^``>B6HC zJ%+8qrI}u0#f$e5T14mu%yPDi!rFuqEMZz+St{ZwxtSi$-ryA&dosU+9NS>)W@&p# zCVzpx<#4ZMop-o=evmFB%m6z-oKUKx3-SWtn05e^m}fRMBHRehatm`yi$I)$ti*v` z6PQ#dMwE~O#%5t|W_gbLepqekUBP(fNpEKtZ!XQ}in+KbM>YA-h+qx;fGioD&+ipC zBe>7VqdGUeAZ_~@l$J#>Ceo58c@#px!0jp$_X$y3f@919;f*D*d;PT~~jBt{X23*&vdG%E01MGSPvx*ocf=wHZYI|E3u4q_Dl}U_6NknE$5Yc<@6}5P93M+*rj5Kj6G>Xj}W-@sd6iy)~b6vrcLAm*D*ggmsQwObLz|n+oY%7}0_b%6Bp#bbgVS#6}iKI!NLjs~(zua}b zzIZR$iD^(^!9vmJim5D2CssTtv-7!`<%m^yLl|R1)@^bG`W{m+Sy&RI!I5FbVmpVW zBZ}l;+HFf8^1{k2E|Bhipa*>h%wU>#F}1}-8@<4A^E%Nfb=w&A&fRv6e%t7;MhHND zVlTh|SgtW=pu*rIVFlwB!UDH$(x%ACo7pt}M)kv%E_eb=B6b zDYxXe6?4)kRwz6(1B*y5yg~pmZE`FjV}KSr;sXZeAu+UEy}Pw_8dFI_j>{|*mltu3 z&s!x#Xka-GHOYsTJ8>fzhm|F)aI-o1F`P>-FhZ5QP9KoFL#?;kufrz|Bq@uCE*&fD zqY*Q1m4GzCMFfG+%*n3+MiP364I62gud!E9&WT;+8eJ0YrUl%~c*k5H;|;LoIRNV1 z2pRdA-sa%p3KVxxyHWk*K36jrtd_WvD$1GgF)a&29;}Y%-6!D#U~+H*P5Fx7);_KP+UwH7UfAH zAV=94{7YRbiw{4_(P$h^4 zu>eUaR-JS4nzQs?npCk%mw~XqiZguiCcyU4ZSQFfS)vs;Jv#i7Jq~R!F>IQX-H;~K zBuK@0g1gjd^;%{4v5R<$6WuUzZ!Q*d;gd5raw|)??T0(SvgC%|V5>kPgQD&s^yJZT zYVGu3&AB;j<*v`n7Y&0PPvIiqLvi;-egNFk4K8c!L?2gMX=+EL8hHUjG_+z=w+;0Q zX;AV;lL$)U3K)V%Jx-kV#vGUWaI1VNhg*Hv@q{L%K`ywtG_%0_eTKtoqYYJZ3~0Q$ z1L z4v@^~#j97j{LSYGEoQ1NKQOZ->FE%`Y)U}q27%Mk8$B1$c>*#+7bL48TDUeUl1L>w zw!&_@H=I^m;5Vz%w%}sbHc3*%+cc5AQ%{ZwFLGc!_YCn~-^qG7->c9-i}@7z&R)5? zPR0qs0oENir($0?Q>2p#pW>ynBw;uk^_ixs7O)U6&0NXP^RdifI6@JbGH#16l<+JJ zg?ZCJ zFT{H2h(+BP1X<^eMzKQGs0YFDfF}Yq%D@s-fEP%x66jn^(k*DimL=uUWMiTU0{_KH zJkK~uQo_cRdO3G-XD96r1;;WJRjKyiVi#j{wh*stItBv2mtk??YW|wN!AM;I4d}fZ z+z5YR1#Sj#=YLrRJ8!Om5k_c`QMQTU&q2zjKB~Kr?L3;s^wT4>1aU>^51|8CoXzXS zVx45dedBJ-<*uw;6BctZ01;C<<6iz6Tt-vy3RV~Tjl4DD^f>~b$LMUXc}#9~)?Zp~ z$+PCQ-WsjcmC$uH#J%2BtNFS4Tu@7tb}P`Dj4&&R*PTLtRaeTG!^IfEO5cLOK@yWi z%M$~_qC2)5GUG6o(d>|5#f-5We`69GI;@*21nCJB-m3b&i$nMJt z)718G2{Er0_z4E znPDVjMZ!Rmp5)?pOn8gMYeS|ZJsNGn5d$VL$VCJ>nHre?qCK@KQj2I3I06pi{ZhiCl+=H2pHAWREm4 zZ@9Nt{HXt?xbxsHHfaPvbdMy6k@D72T{;QB-+LuMq=9cyLi0EdYK5Qo{K7RaPITR& z3M9HnNrNgEt0?AyB|J@5u|jVCs><`&bU|H7#RhkK%i+w*9Go$5#fAKt9O9YT8{X`W z=Yb5NuJrNnBsS$_GdZ_wh5fnPbBJ&gi3LGP;Se-lOhzkjF z)+MqDxv=~Y&Kj~p*IcztrITz7cn4LqPGnP5MTmcU$s4D`fjDsm@&pIuMp15#K=j9; z>=h-zXtz6os`!WHf}1wMiPjGv_bpmdYdcC0qM{F%uH&9B6f&S30fF7S4pu9xtf;1< z9E@ICUMw!oVwJwKxL5@1Mr*-pxq>9HB5)$>5EQ|-Y~#e;ZO`IFPOT_fC`h3Jlg};s z;BkiY9IB9;U0J4_Dnv8b4O1e8(;f)$36<=(0lVQv~AmWOiaCHIdE5e2r zF-^SM^EV+Q$?i**RVY)KgDV7VyMzqA%ri?%^B7`rmjzZl3wdzXhC5P|I{af@fZMt# z7dH4R?88<~rVTN(4mCcegb{7eJy#lcgz9E}HY7z#BojwxGLFQq2Pl)Zy90fI|E*=H zw|R^}5Yi)y(emoy9+-NM@Olm#^y?@Ep^n2XSNidemYDbx{e_h)+HoOguL>EVaDW`2 zgh#!3=wEHj=D~v#^@!&=E+=!*!r{bLZ3ljLbOf-jal)i2NCsPak1pxRNAoDr$B5vw zQv>+t?pY*Rq6*zzZP7s3As$xfe3>LgxttlgNwGQNvd0=ItQ4|9bneblSIBPr)GKEU z6rZhvW{OZ~KL!sJEEh8uMtp?aBULtBgH3Kv_OYd#&^EX)QQct8V2a`dC= z4KIA|%|?Kd8(4921TWuQe&*W3VgVOP{n+a(S3t(k%7kEd3=kuF${HOWbFM1qC`RX% zN(4)gl5n~=&?%0aLx=|hIEgxJixHa|Q}o1k3>ZVY3J7IK;>0YR`_r~cl62+WfcdL0 z>v02`u0(}?5lUnui~TvQK(RzN+TC*?I>--Y6=qVclZ>o6U4fI(sh5Q3>X2NqBZI1o zh=MXWEB{Z)|7R85c$q8zMLZY=Ro3+aj-GEj>pU{xRI6I6vR3?HZqQLOd zk=e?gmtUeA%PkgR-GK3AP&NF3K(>Ds_$l5HcLV;>tS9q1f8v(kgvZ3dIz@~OF4ot` zh61C~gI)DntXowaP#dAbKkr_+Uvv$^A(C2Xwg+(mLkn-P*x}WP8R5-qSXrhLp|dus zrp`fwTtEC045*G)cHj)Uy{wR5y#04W%QM69Y9TS)5_g?)s$Z1@AEJ%!8ypfKQwuFf zqtPKQabYxo4W0HJSfZDT2wn;yv6Q0cT3nIPF3w)eO2DJRt4f0*D%auR6%ZIjbI*vM z5x~5_h|5e{fD8k`6c*v@79sP({#t1R`^+h(mMWWcRS00{F_nj#kku&IBS_<4=-<(b zDX2?*d7T4dOvJsPv^==f z!2-6E)ko+jKj#n-B)Koh@=`7WN1;`CjQKgTZDS^={dtsIAl>~5kn|axv#AwAW#wQH zMj2{HEZZ%^Py#JB1ePe~Y8D{69rA!}c{^H8?-YaFxTNm4#72oth;I4@$|Bs)E<@Ue z1Jz~tHbtpk4}_;`6@943a`3q^6(Ph(j!j2KhS_sxMGU2RHdNafE>;sKpN9vu{u+$O zVJ!bF?*Hn=QqG<9mvWJ@92`SLdFd4$#1L;*tjl1 zJC$FU%iUI3vIIoLAlFgpg_t{gMKuR1zzWVw;UG^aIQ0pVaf~C0jbz`TvC1uDqr@j+ zQ14ikM4(F7g;XQ-h;P8?WjWZRL!wfsVMC^qa8HZZ#@57Yw{4aHYo$i5eiw8aAyw?G z#VV71lWsUP8?G=!dhyD`BYHXtwV*Iqxx)iG-220kBYgfx!^Z2rV&=cw<`>7u{+n|? zRM-GV^`Rfuh-}#)WAenaSzF~LVq7D%tH~T01glM$l$YT80hxHqRl6l`y-+?}rPy`1vo5+CjbEDaN`w1olt>77f2(;Be?;UPVB!vlS!?$@s; znjlP&&a&GsONv_Jva}~d_l0`3HZ?vKs@klCHC<)eQ?(VX#Jlw_7bm)DM=~F;z5=n9$t4K*J~U z6h$M5C01??+X34A3|8$Gw#)>4dN5~H8EH|RlMKQsBwA6MIycWL*TZ4LY6C2#yMg;) ztUe&RvQro3J4OYbLV*QI^pc7X(AgmXk=LpnVKo}vBn@(EFJrIyYc zd8q9b+zxeG@U75k0bmfP-}(?ZNno)+v_~Ewt0Tt*rhiSE4e+bg0NsP(;4n`3sSm&q zBJ{fD=NvN6M&ukaa8aj66Sh}bxzz5#rxGpnFoRoUwgSD9J6^UlqQt020zlXo4|65? zAL=VCbmD6!IWz+Z7($OuAc6$Z_sDAW{7$e0EK{_#*0~3v0K_P>GOiRBZ?qRzo~BMB z#5@lA<9hJ^APD{npZkLQpFjTo4+JL==I71(gAxe%#B!3U_lU_XA;pY|RzmIJ`bnk)RJ^Fh`{!_Ga<@m!d zJUDawGSVMrCVXB!8l0HM_ssDV_m2MVk>4XMk4Q=oyp6Q&WZKN}rC^|+gz?AtyZu12 zPi%wn+lW7j@0sK8zjx$!(E|EKIGOe`(k|Vff-`sg(HGwPwPU~kp1=K|)%|Dh2~NyO z9mgVkv3%xOaN_Gj;$KDlDK9>Q-^~9O;O5&-3Lm^1QM`9>dL#k>`0C zrx^d%=gRwn6Z{RjN%VOe@fW3x#uK;f+iUL+PP~{N^SR?0#iR_Kj2-h{J|3JPz4DLY zc^9^}ZzIflt!>NqRm30j%Ds>ZznBUmYj6~QR_{wl_s7xd{WHXq?hG$KLAZi&L^EC* zeg>bEv_YIPjq}dnw)&p_d98m~ecMRONSU}k!a}^fgtP&AnK_=ZdjAaRZ>GkNb+PPg z9}G_P@I5#O1DRvY!%rQ5i9D!3gE*!?`k~;&Z>7^^9>?_hJIJ%KOP+s&JbyGIkM;G{ z4+kfv(s^c%pS1LUh4e=yJ;D=aay({7AHrOgbfHU0vvI$Sv}18vL=T7x{CLuX&qLYX z+yl%N1mDIVD*amz#vE<-c;wd^qFb= zu$;k}u7ly1k?-M;1SeRZg(ndv92UmUfVg8{n12rG$|sV2vGh%(EB$$EtmBz1&(CH2 z`E)i6woC5~g4am@u4^FvszPyB81!^X?fIA79beuTQccz}~^hizJ!VetJK;5qhD zT|1bUVSc9Zw^O-?KyW8f?j-WDUWS(tCTds@!?zJ;zx{d`&(DMSvwS_|)A5hz3-i5# z`0L1L{eoV_u zv&SEK;XPl2qUkT(@8@SMF>p&x_q)xolnTw-k?OR(2A3^yn54>I%8Mm)!@?dQFCbai0NA+HBV ze@W*m>tpy~7xu@1@%#{8i%-`_eEb0UPNBT=&7_RQv?ZjGKd~&sw-F|P;@o8T1%%(- zac*9gImf))8Qa4}<0g1#+AB!gMx8s!gUrWIYV3uknfAypTmA98O_mv?2TRxWFNTY_ zwKSi;iBHB~M_aGpcM4v?H`Cuiy3$8fr_d!v_1?DEFF_^V)Xnx%FA;A%Z=^G3#dgMpM?G7k>ro$qszi z+GhE>luxvM()z@7pI`ZXIEFL}zxrsrz7po|Abe5kjpvm0h2;!hACA8-@kyHS;UJw` zIX~XV`KWlp>GNy0d+9^?51()0FHJYBhw1O2AN=jNYvt1I{&mG?1Rl{}32ejKWq&39 zSL5}F;S&gRJXr_BpFvp1$s2#S|1}&`Tpei~^bkJ^H^gr`KcXCuj%i{T?A9*x55w^b zsdoLbd6PJk z(7?CQ_fz=IIvIW)VXZUf4VHG|Q-gXJs|Uq}1}yI+@;;3?f6u`Fu#K-Hjcxkl$#{Nz zm@R)g-rD>w>N$x-Yx8Y{D+v2-T3*(X!5_o^yyckv#YeD5`nBN1ALDmwtVJ&PedsG# zj`{du87s%|J^UqPS5F=#EwS9Olct~ZXu|PircWZB?8D(iWJPOp4r%;Nd>9P9IBz2U zloxO15hd{Z^?T)k8;-5@ z_oZ}tVt+6x@`~ksWtY6fi%$a?gn#l2=p*p_XnKuvyo+U+haZ!d{5qI^353}Dw8tMw znD~&UE!_|M<%y%`k^i16@6Qd!e=`-Iars|C{CWIN(4T{u&9f{&{PO38pT=?Y)6)21 z_~DOXzl^p{;kSkHQ|5smX1xrj`*s`Uk9|h>tkyT?V|)hj5HW>L80Lp*mXEk${G-U1 z<`0(Nk59MH@LMQ9jq;~l-3)&jVbY+bKaX(63x5k?w#`50e+}Vd`0c~R`0pbAx)+~r zpE$YAMe?)81CqRzX(y5PhScGBIT+EfdR|7}m!>RlT+gt)Z?MhBf)hWLdPnDF|9*nJ zlbN6>K92{ccpURH@Aj{w&NQEj z)*R)9%=dejf)n4{vCekX{*}jv)R}1itM?hKOHTwRK6#XV8U^b{RB!JJ=Ejko@@_|Y zzn8D5P`Y2~5*LObt|`hj4y&RHH?_eO;YpAVxC z)AxZFhOIA#pVu9}qH{iAJA(J7@k`}LtUJA~y!HvqA>{Mt7{`_I7ZLweX)}pmw$1eK zB7IKMW8P?KKSf$5l}5N&?vaH-9WLYN5$~@<96QE8jrea$UEX|&=Bv^y^YPQeUxNOf zjA-d;nk7z{_O64;V9YEU{4p=LI!IbRFSjz6QAX)N%UC?J2R?ocM~pAx&mTL3hiUxn zhlv}%tj^CE_kMeqk$2{}?n(VJ>Hb^V=K*`VUm4WFK5rmR>+|qGbNpqn&l(y2%k`kUy)5-A zJ~)RN{~g4COX58mista7$id9FbR@_ey`TLZp3mSb0*;K=j>RKtK&K&aA^)in`G>{7 zJR<)ah`;F7L;SLSen?LiK3a_Fi|G#`{8gm;e8cj76KTY+g(d(ue@eeKURo@x$_?>K)}$_VH14<|ULT zOzbzq#Rq~+9lt?@zP*Fsgz4W$`YEKd48!jr?6*&xF#eGTgUq(nlf((rzKOK-oMil~ zfJN6a?W4i?J>=(aYx8x4=cVp=99aIL_XL^eQ*9bN@1pLd5os@?+~rgn+w*xQ!^K~N z40KE7B-YFD9Ksu^vV@aquOf}*jAvMx?;@Vt3ZDi!KUwBYqz&xdJ)N7u@slT2KDY3{ zPlbPq@KMxh>A~$}_^1mTOve!ZUJ9nDEaJfm;<{4dKSo(U{ZCS1WRK)L_K_by|FCcZ zc4XKsOjvz*Pk7l9`bMRP_N}fmSCrRhWqI+DetyEEg_ullcn7o zl4kXM*VSVeV>QBN5bI5t1>2$zC(DG^__HX?XoYo@#oKb!ynW5dl;<|7K=By znEz>sPtq3q;qxul`TkSbmw(xKj`B>_!}uQ|{>%8C8ef@9Oy|e?!0=qj#yO^y4Dv9o zjHJ7P_`?gc4;KC$e){_j_SMqP zNLpg9W;h=#{qyN`Pe;wUX8D@we9<~GbDV3o;psK>hj_7me+Oaywtidu>xlRFRR;5$ zi2uIiPr!WAVE$$W_NW+$ge>X7Z1o&lA6k#hCx3hi&)Y;2KF9Fq!^!y9QID?m@mZkd zKRzTs>+xxgc(ME(GrzqI!qy(sEZ=7&K0d#)w1+ngmY7~G4MpU9%;yKz_6EvrBi-`8 zjW9$Uv_04c>$xInaU1N9!L}-C$+59^kR)rXpZ^)jpQLSrgZY)N;*4BS5Wxck% zarW^@`ZGNGZU0XaKf3LVq{nTuA3kj`{8Lo^U`_cQ;bu64@U2u?!p$^(ly4>4jn+6n zpWHkf$1`b==*2`LxvD-2&(B|d5|Q5X9%3>SX9!G^5C%aT6{o8gM-9Z8Sd zO4GQ^#k0%_9WVY|+h8MDn-G56rIFr{mWXrt1%Bqchd!h0R)?$!R-Q1k4;CiPSU-Ij z5_}0G!E#nc>2I*FmaZ_yef8V(=^~8-=KIk!^cjGkeKxo({*On*Bde3a;yID#AD7O- zp5VQ@uVsAov(v@mHi-%Fy9Z5?_WqAUcN|obyE7ld@t666Lpcvy?lv2PYSM> z_oofWI!XCm_$$*+v>=nF>w)Y~oHL!DkK)g8Zt;(_le(QWP8fdq0{Vq~^o^$4QcF~rBgp`Z4x$r z{xIM30P;jyS0*t(yfK_Reo^p}Zu=)F`|j2tPhdPY zk_ldsZu?4Q1@gB@;#;F5uuklXoD@IVNG$1y&z%>UEyYviGU4>!94y!$>7 z)MdO<4Nu(iRQ1HIRvj;lbSLOvcjB396JGJV6I-p$9dUv-acyDcbg$K_!zod%IZ^5~ zPG_f;8c#C-$7$8m)zWHhqP6$*4h(r*o9odzM7>E zA{2IYOu$VF9syu`@UFI+rQ@0|2xr8HQ#m*ifT8enpNQ}Xlk4KV|9J}^k?>0|TbNuL z>vR;x0;-=3zmA9{1oUFq4r};5{0iQV!p9_h>_e8W7t7)>C35iTDE)mBu4gPixd=Z; zGyMNZ@Bw~dLA5Y<5PtaQHSGRBqTPt%jszc!e)GqX;6thKhf`s4$@p_5_(&YUuOlL@ z#ZgBBX9(${j)+v~r++LC;MWmY{JRm6_@lx9^H=2m_{ey1prG20K2f?GKd#!7Nc~F} zz8~S=K=}J!`W1w6uiMpkf3Sk^#rxwh^Z(dOuOR$yyzn-{|IrKoJ%p#c@_&f%FM8pB zgfQ-`MeY4lg#U&Y{u6}HcwzQ84bPWT={DX$@bkf&$S+Ddemi^_y-a9lmY;z+Z;o{{^o+<^8X@Fvo}eeZ@;> z{jYi9Payw)o67&Xm;PC#|Kyi~42pp|-Y50F=H@TyVH)WcCOmA<@(0LYK)QXbzx3;& zarXyhr2i0MmNR<#iI?6$`oBXs(#JgD=77&w(Y9AF+Mn_bkGzUiz01{?S8m`foG*;UGg9j`@XNy!!tR(*H5S(@3}W z-}2J`0n%@K_5XJWKk0>8-`ie(_IK%l7=DHcAHMKWdN6)!;c_aS@Jn`=pYZ!(w(o}t zpN0Pv@Y%=chyGGD?*8DF#^nZXbeXm%1-+_-6tN#l~|H1oge2xVbgfFAM%dWmHgy+yd6qow%G2ILQ z0m4_k@PCQ$6)*hXAiU&-{~5ybUieQDUiQL2M0mjq6Cd;mgD-sU5B@i#|2e{Q64Lqd zu&Uf7<}Zf71*9ON77Zh5r|XSG@4QMfjE%J_-@&TONKs zj_|#&1SkFqqR4%~=QP5Xkl*NO2H_{X@B+fuz3|frf8GnTfAp8bZjc@x#s57o%yjzA zVXCFS@cuab4C;FY^l%xeq=(-|_-WK{EY7xxIX5;?1h>Cx))|T zeeU23ABDH*g_%wtJd*6vpZCH{$DQ3o`aLiF2Y~Z+hV$BL5Qdf7+{`zJ2`q-|)h0pFV#4^mn{4)9LfaPha}^IQ$>c z-YeexeIIlYFMdPyNQCzc!e2!?_sce)>GQ|xWBv5=?n$HG@o{y#%_4iUx=-}2H0 zelPq*IW%GfY1~ewaRg{P3$EPKN2v$4{sK9t;07!1DmYR6SV#E_&%dM*1}7FAEsGV10BoeA$p18~^g({*hsp2!F!_mv&ww7^L|E6OBf$c~-$$4`UD9U<;nxu6&Y9sq zK=@^ZxzjfKW!UN?Ju|-_W_^B`?OFIW)Mp=)7wNA`<8=PCy)fqueH>Z(KTVZq`)_&m zGe3O{S$?MfzzhEo%Afp0Jv*@eFs%8H1pjhK{l&i7i@7<~+%^etrR?El+d_+S4j_Vb{p`w)L0HpV|f z`PV)c%hSxik?Jq~3HkLw&5;PRyr#b|cxMPa^d;n%f7A>AhavgtL&)+U0i*pi!v22X zx|dFT(8rMF|0ME%0b!p%7rk`mrw<~_&;GVk;d`n6(-)EDKZT0>2u~uy){mFG^v96? z0_e$~FK?&nqYopiZw~qYA;QZ4}N=oJ?jTzQp$==;d(dkXpgPlPXf z^*#Ixarzq4Z+q|&AM}xA<%y5WUYP0hk>sa8?S+|6pGlU^FnuFgnDu=%6^3&gm#*;B z7m}ax)Q$?_B4 zcf2si_fgzw@zd$I$ig!IUYPUi4b=ZWWH9<-So7=nvVA|y{`ujTp11J*!JiJnFZ~{A zejSg8yfERXA0$ivB~__Q(8iN~jx7CuM*XJ|WBn1{L1gL z*JkOJpJE=x{6xR$=nRXl7)6%8%V5zJv&ho-3;mEzb)Q*X~dzw&<{`d`X>Fe zSpDH~Pg0RSvA=uStV}d{mgnt`P$mOl9U>A2tM&pT(Zr;3Ra(|?Eh4}tIDMr8k(w99DjwK0o<4@{d>4*K>3Fx*?bT#ACDk<(~{P zzLZUzzl43BJTac3{fgz{>0bXYGkD$%{EFQY`PE*K!Dr1NKgEaf<!0$JB9Q?4s7lFTMu)j51!4)^Eq z7fg9de`^k(Xz)JT8?QLOH?E8I^ZuyfUU?orA1_&&Va@L+-#C5$Q`qx+|9bIjqJ8)N z^J3#qAD$T}?4Um%Q!@E4g>LQ9NuGbV&wua!A@GGB@OSV2P2iHj7lIoGkAt-q^Vi<} z9dLH}@^3KZUjqFL2CKhrG58ATQwCoJe!$=lfq!7|N5D@Rd;@r&!Jh)Za4^bmcJfEy zuN(XY@J$AP3B1VQJHf9r_#5Ek489-yVS{&rHyiw2@COb40eG#!KL(E)ya&vhw5uOK z1HaGUUw|(*_?O^)2JZv^z~H%Tbbj057lVIou=F`umX)Xem}BtE;a_C%5#ZMu{95oy z1|JJvYw#Pv?=pB9c!$An27kriRp31azZJYgyThr#bNcnth&gKOZUPRQ!l2EWJPN$?JX-vj;!gMSY^W$;ztUmN@(@Y4o= z1U!31R-W3!k3_oqas%|&2ORz3UPJ#h^b-wM`W*(}0{xE-{xk3w4ZZ_BWw7M^tsK5L zhp*1zo*e$L8L!IkI)nd$^5PZk*#jp1-{t5^-}BKZ4{vW3JJ9Fh?Jd8x=wjL1&DdAF ze<|$wy}eyx?CsBUWgA8`g^mJtuxRS>oC&W z(60yE^`v4SM*4f8e+;Z_;`mYw#7O@Dgqy*tccd#OVx(UK{XXz6LsuNcNdGAGNwDG~ z#`KDR80nvb{sHivCcR=FM*5#YUk$eD73VP0{|fq9V8uOD{`Y`8r}!8b!m9W8S7rR_ zzmuWI^rG*{>8oNRTKaWE8U7pcAN#cb4ZQX%l%L`wM*iUZGKN1to9|RPp9|hu*&o4*-x6G{)!{$>9dpPv0r#BSZq3#2f!CPyPaFDX=#Luta5gG=!eF&mF$!aT zBha4%_lEpLSDeB~SN%>l_+t3^SpY{URX=Zk6u;2&pKGx4QyfD}f7D>n6@$>y7oC}5 z(G`=>(oZy4bj2TxbYaExvsiKbB365DGFbeI;b-ZRzsG()QhPo2&(9=JkNtW;@{aiZ zl25pzek<;uzcoAg2=u$b7jh$xzgKL?=&rp6>%94qVAVV7li~u#{60l~L*QPM--U+$ zMd𝔐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