Skip to main content

joycon_driver/
stick.rs

1use nom::{combinator::eof, sequence::tuple};
2
3use crate::{
4    error::{JoyConDriverError, JoyConDriverResult},
5    joycon::{JoyConDevice, SUB_COMMAND_READ_HEADER_BYTES, USER_CALIBRATION_DATA_MAGIC},
6    parser::{parse_stick_data, parse_stick_data_var},
7};
8
9#[derive(Debug, Clone)]
10pub struct StickData {
11    pub horizontal: u16,
12    pub vertical: u16,
13}
14
15impl TryFrom<&[u8]> for StickData {
16    type Error = JoyConDriverError;
17
18    fn try_from(buf: &[u8]) -> JoyConDriverResult<Self> {
19        let (_, ((horizontal, vertical), _)) = tuple((parse_stick_data_var, eof))(buf)
20            .map_err(|_| JoyConDriverError::InvalidResultBuffer)?;
21
22        Ok(Self {
23            horizontal,
24            vertical,
25        })
26    }
27}
28
29#[derive(Debug, Clone, Copy)]
30pub enum StickDirection {
31    Top,
32    TopRight,
33    Right,
34    BottomRight,
35    Bottom,
36    BottomLeft,
37    Left,
38    TopLeft,
39    Neutral,
40}
41
42impl TryFrom<u8> for StickDirection {
43    type Error = JoyConDriverError;
44
45    fn try_from(data: u8) -> JoyConDriverResult<Self> {
46        Ok(match data {
47            0 => StickDirection::Top,
48            1 => StickDirection::TopRight,
49            2 => StickDirection::Right,
50            3 => StickDirection::BottomRight,
51            4 => StickDirection::Bottom,
52            5 => StickDirection::BottomLeft,
53            6 => StickDirection::Left,
54            7 => StickDirection::TopLeft,
55            8 => StickDirection::Neutral,
56            _ => return Err(JoyConDriverError::InvalidResultBuffer),
57        })
58    }
59}
60
61#[derive(Debug, Clone, Copy)]
62pub struct StickAxisCalibration {
63    pub max: u16,
64    pub center: u16,
65    pub min: u16,
66}
67
68impl StickAxisCalibration {
69    fn from_data(center: u16, max_above_center: u16, min_below_center: u16) -> Self {
70        Self {
71            max: center.saturating_add(max_above_center),
72            center,
73            min: center.saturating_sub(min_below_center),
74        }
75    }
76}
77
78#[derive(Debug, Clone)]
79pub struct StickCalibration {
80    pub x: StickAxisCalibration,
81    pub y: StickAxisCalibration,
82}
83
84impl StickCalibration {
85    const DATA_BYTES: usize = 9;
86
87    pub fn read_left_factory_data(device: &JoyConDevice) -> JoyConDriverResult<Option<Self>> {
88        let mut buf = [0u8; SUB_COMMAND_READ_HEADER_BYTES + Self::DATA_BYTES];
89        device.read(&mut buf, 0x603D)?;
90        Self::try_from_left(&buf[SUB_COMMAND_READ_HEADER_BYTES..])
91    }
92
93    pub fn read_left_user_data(device: &JoyConDevice) -> JoyConDriverResult<Option<Self>> {
94        let mut buf = [0u8; SUB_COMMAND_READ_HEADER_BYTES
95            + USER_CALIBRATION_DATA_MAGIC.len()
96            + Self::DATA_BYTES];
97        device.read(&mut buf, 0x8010)?;
98        const DATA_START: usize = SUB_COMMAND_READ_HEADER_BYTES + USER_CALIBRATION_DATA_MAGIC.len();
99        if buf[SUB_COMMAND_READ_HEADER_BYTES..DATA_START] == USER_CALIBRATION_DATA_MAGIC {
100            Self::try_from_left(&buf[DATA_START..])
101        } else {
102            Ok(None)
103        }
104    }
105
106    fn try_from_left(buf: &[u8]) -> JoyConDriverResult<Option<Self>> {
107        if buf.iter().all(|b| *b == 0xFF) {
108            return Ok(None);
109        }
110
111        let (_, (max_above_center, center, min_below_center, _)) =
112            tuple((parse_stick_data, parse_stick_data, parse_stick_data, eof))(buf)
113                .map_err(|_| JoyConDriverError::InvalidResultBuffer)?;
114
115        Ok(Some(Self {
116            x: StickAxisCalibration::from_data(center.0, max_above_center.0, min_below_center.0),
117            y: StickAxisCalibration::from_data(center.1, max_above_center.1, min_below_center.1),
118        }))
119    }
120
121    pub fn read_right_factory_data(device: &JoyConDevice) -> JoyConDriverResult<Option<Self>> {
122        let mut buf = [0u8; SUB_COMMAND_READ_HEADER_BYTES + Self::DATA_BYTES];
123        device.read(&mut buf, 0x6046)?;
124        Self::try_from_right(&buf[SUB_COMMAND_READ_HEADER_BYTES..])
125    }
126
127    pub fn read_right_user_data(device: &JoyConDevice) -> JoyConDriverResult<Option<Self>> {
128        let mut buf = [0u8; SUB_COMMAND_READ_HEADER_BYTES
129            + USER_CALIBRATION_DATA_MAGIC.len()
130            + Self::DATA_BYTES];
131        device.read(&mut buf, 0x801B)?;
132        const DATA_START: usize = SUB_COMMAND_READ_HEADER_BYTES + USER_CALIBRATION_DATA_MAGIC.len();
133        if buf[SUB_COMMAND_READ_HEADER_BYTES..DATA_START] == USER_CALIBRATION_DATA_MAGIC {
134            Self::try_from_left(&buf[DATA_START..])
135        } else {
136            Ok(None)
137        }
138    }
139
140    fn try_from_right(buf: &[u8]) -> JoyConDriverResult<Option<Self>> {
141        if buf.iter().all(|b| *b == 0xFF) {
142            return Ok(None);
143        }
144
145        let (_, (center, min_below_center, max_above_center, _)) =
146            tuple((parse_stick_data, parse_stick_data, parse_stick_data, eof))(buf)
147                .map_err(|_| JoyConDriverError::InvalidResultBuffer)?;
148
149        Ok(Some(Self {
150            x: StickAxisCalibration::from_data(center.0, max_above_center.0, min_below_center.0),
151            y: StickAxisCalibration::from_data(center.1, max_above_center.1, min_below_center.1),
152        }))
153    }
154}