joycon-driver 0.0.0

Driver for Nintendo Switchâ„¢
Documentation
use nom::{combinator::eof, sequence::tuple};

use crate::{
    error::{JoyConDriverError, JoyConDriverResult},
    joycon::{JoyConDevice, SUB_COMMAND_READ_HEADER_BYTES, USER_CALIBRATION_DATA_MAGIC},
    parser::{parse_stick_data, parse_stick_data_var},
};

#[derive(Debug, Clone)]
pub struct StickData {
    pub horizontal: u16,
    pub vertical: u16,
}

impl TryFrom<&[u8]> for StickData {
    type Error = JoyConDriverError;

    fn try_from(buf: &[u8]) -> JoyConDriverResult<Self> {
        let (_, ((horizontal, vertical), _)) = tuple((parse_stick_data_var, eof))(buf)
            .map_err(|_| JoyConDriverError::InvalidResultBuffer)?;

        Ok(Self {
            horizontal,
            vertical,
        })
    }
}

#[derive(Debug, Clone, Copy)]
pub enum StickDirection {
    Top,
    TopRight,
    Right,
    BottomRight,
    Bottom,
    BottomLeft,
    Left,
    TopLeft,
    Neutral,
}

impl TryFrom<u8> for StickDirection {
    type Error = JoyConDriverError;

    fn try_from(data: u8) -> JoyConDriverResult<Self> {
        Ok(match data {
            0 => StickDirection::Top,
            1 => StickDirection::TopRight,
            2 => StickDirection::Right,
            3 => StickDirection::BottomRight,
            4 => StickDirection::Bottom,
            5 => StickDirection::BottomLeft,
            6 => StickDirection::Left,
            7 => StickDirection::TopLeft,
            8 => StickDirection::Neutral,
            _ => return Err(JoyConDriverError::InvalidResultBuffer),
        })
    }
}

#[derive(Debug, Clone, Copy)]
pub struct StickAxisCalibration {
    pub max: u16,
    pub center: u16,
    pub min: u16,
}

impl StickAxisCalibration {
    fn from_data(center: u16, max_above_center: u16, min_below_center: u16) -> Self {
        Self {
            max: center.saturating_add(max_above_center),
            center,
            min: center.saturating_sub(min_below_center),
        }
    }
}

#[derive(Debug, Clone)]
pub struct StickCalibration {
    pub x: StickAxisCalibration,
    pub y: StickAxisCalibration,
}

impl StickCalibration {
    const DATA_BYTES: usize = 9;

    pub fn read_left_factory_data(device: &JoyConDevice) -> JoyConDriverResult<Option<Self>> {
        let mut buf = [0u8; SUB_COMMAND_READ_HEADER_BYTES + Self::DATA_BYTES];
        device.read(&mut buf, 0x603D)?;
        Self::try_from_left(&buf[SUB_COMMAND_READ_HEADER_BYTES..])
    }

    pub fn read_left_user_data(device: &JoyConDevice) -> JoyConDriverResult<Option<Self>> {
        let mut buf = [0u8; SUB_COMMAND_READ_HEADER_BYTES
            + USER_CALIBRATION_DATA_MAGIC.len()
            + Self::DATA_BYTES];
        device.read(&mut buf, 0x8010)?;
        const DATA_START: usize = SUB_COMMAND_READ_HEADER_BYTES + USER_CALIBRATION_DATA_MAGIC.len();
        if buf[SUB_COMMAND_READ_HEADER_BYTES..DATA_START] == USER_CALIBRATION_DATA_MAGIC {
            Self::try_from_left(&buf[DATA_START..])
        } else {
            Ok(None)
        }
    }

    fn try_from_left(buf: &[u8]) -> JoyConDriverResult<Option<Self>> {
        if buf.iter().all(|b| *b == 0xFF) {
            return Ok(None);
        }

        let (_, (max_above_center, center, min_below_center, _)) =
            tuple((parse_stick_data, parse_stick_data, parse_stick_data, eof))(buf)
                .map_err(|_| JoyConDriverError::InvalidResultBuffer)?;

        Ok(Some(Self {
            x: StickAxisCalibration::from_data(center.0, max_above_center.0, min_below_center.0),
            y: StickAxisCalibration::from_data(center.1, max_above_center.1, min_below_center.1),
        }))
    }

    pub fn read_right_factory_data(device: &JoyConDevice) -> JoyConDriverResult<Option<Self>> {
        let mut buf = [0u8; SUB_COMMAND_READ_HEADER_BYTES + Self::DATA_BYTES];
        device.read(&mut buf, 0x6046)?;
        Self::try_from_right(&buf[SUB_COMMAND_READ_HEADER_BYTES..])
    }

    pub fn read_right_user_data(device: &JoyConDevice) -> JoyConDriverResult<Option<Self>> {
        let mut buf = [0u8; SUB_COMMAND_READ_HEADER_BYTES
            + USER_CALIBRATION_DATA_MAGIC.len()
            + Self::DATA_BYTES];
        device.read(&mut buf, 0x801B)?;
        const DATA_START: usize = SUB_COMMAND_READ_HEADER_BYTES + USER_CALIBRATION_DATA_MAGIC.len();
        if buf[SUB_COMMAND_READ_HEADER_BYTES..DATA_START] == USER_CALIBRATION_DATA_MAGIC {
            Self::try_from_left(&buf[DATA_START..])
        } else {
            Ok(None)
        }
    }

    fn try_from_right(buf: &[u8]) -> JoyConDriverResult<Option<Self>> {
        if buf.iter().all(|b| *b == 0xFF) {
            return Ok(None);
        }

        let (_, (center, min_below_center, max_above_center, _)) =
            tuple((parse_stick_data, parse_stick_data, parse_stick_data, eof))(buf)
                .map_err(|_| JoyConDriverError::InvalidResultBuffer)?;

        Ok(Some(Self {
            x: StickAxisCalibration::from_data(center.0, max_above_center.0, min_below_center.0),
            y: StickAxisCalibration::from_data(center.1, max_above_center.1, min_below_center.1),
        }))
    }
}