1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
pub mod internals;

use internals::*;
use std::io::{Cursor, Result, Error, ErrorKind};
use byteorder::{WriteBytesExt, LittleEndian};

pub const PROTOCOL_VERSION: u16 = 1001;

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MessageSource {
  Server,
  Client
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MessageType {
  ProtocolVersion,
  ConnectedControllers,
  ControllerData
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum SlotState {
  NotConnected,
  Reserved,
  Connected
}

impl Default for SlotState {
  fn default() -> SlotState {
    SlotState::NotConnected
  }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum DeviceType {
  NotApplicable,
  PartialGyro,
  FullGyro
}

impl Default for DeviceType {
  fn default() -> DeviceType {
    DeviceType::NotApplicable
  }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ConnectionType {
  NotApplicable,
  USB,
  Bluetooth
}

impl Default for ConnectionType {
  fn default() -> ConnectionType {
    ConnectionType::NotApplicable
  }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BatteryStatus {
  NotApplicable,
  Dying,
  Low,
  Medium,
  High,
  Full,
  Charging,
  Charged
}

impl Default for BatteryStatus {
  fn default() -> BatteryStatus {
    BatteryStatus::NotApplicable
  }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ControllerDataRequest {
  ReportAll,
  SlotNumber(u8),
  MAC(u64)
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MessagePayload {
  None,
  ProtocolVersion(u16),
  ConnectedControllersRequest { amount: i32, 
                                slot_numbers: [u8; 4] },
  ConnectedControllerResponse { controller_info: ControllerInfo },
  ControllerDataRequest(ControllerDataRequest),
  ControllerData { packet_number: u32,
                   controller_info: ControllerInfo,
                   controller_data: ControllerData }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MessageHeader {
  pub source: MessageSource,
  pub protocol_version: u16,
  pub message_length: u16,
  pub checksum: u32,
  pub source_id: u32
}

#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct ControllerInfo {
  pub slot: u8,
  pub slot_state: SlotState,
  pub device_type: DeviceType,
  pub connection_type: ConnectionType,
  pub mac_address: u64,
  pub battery_status: BatteryStatus
}

#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct TouchData {
  pub active: bool,
  pub id: u8,
  pub position_x: u16,
  pub position_y: u16
}

#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct ControllerData {
  pub connected: bool,
  pub d_pad_left: bool,
  pub d_pad_down: bool,
  pub d_pad_right: bool,
  pub d_pad_up: bool,
  pub start: bool,
  pub right_stick_button: bool,
  pub left_stick_button: bool,
  pub select: bool,
  pub square: bool,
  pub cross: bool,
  pub circle: bool,
  pub triangle: bool,
  pub r1: bool,
  pub l1: bool,
  pub r2: bool,
  pub l2: bool,
  pub ps: u8,
  pub touch: u8,
  pub left_stick_x: u8,
  pub left_stick_y: u8,
  pub right_stick_x: u8,
  pub right_stick_y: u8,
  pub analog_d_pad_left: u8,
  pub analog_d_pad_down: u8,
  pub analog_d_pad_right: u8,
  pub analog_d_pad_up: u8,
  pub analog_square: u8,
  pub analog_triangle: u8,
  pub analog_cross: u8,
  pub analog_circle: u8,
  pub analog_r1: u8,
  pub analog_l1: u8,
  pub analog_r2: u8,
  pub analog_l2: u8,
  pub first_touch: TouchData,
  pub second_touch: TouchData,
  pub motion_data_timestamp: u64,
  pub accelerometer_x: f32,
  pub accelerometer_y: f32,
  pub accelerometer_z: f32,
  pub gyroscope_pitch: f32,
  pub gyroscope_yaw: f32,
  pub gyroscope_roll: f32,
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Message {
  pub header: MessageHeader,
  pub message_type: MessageType,
  pub payload: MessagePayload
}

fn compute_checksum(packet: &[u8]) -> u32 {
  let mut packet = packet.to_vec();
  for byte in &mut packet[8..12] {
      *byte = 0;
  }

  crc::crc32::checksum_ieee(&packet)
}

pub fn encode_message(writer: &mut Vec<u8>, message: Message) -> Result<()> {
  encode_message_header(writer, message.header)?;
  encode_message_type(writer, message.message_type)?;
  encode_message_payload(writer, message.payload)?;

  let length = (writer.len() - 16) as u16;
  let mut length_bytes = vec![];
  length_bytes.write_u16::<LittleEndian>(length)?;
  writer[6..8].swap_with_slice(&mut length_bytes[..]);

  let checksum = compute_checksum(writer);
  let mut checksum_bytes = vec![];
  checksum_bytes.write_u32::<LittleEndian>(checksum)?;
  writer[8..12].swap_with_slice(&mut checksum_bytes[..]);

  Ok(())
}

pub fn parse_message(message_source: MessageSource,
                     packet: &[u8], 
                     verify_checksum: bool) -> Result<Message> {
  let mut reader = Cursor::new(packet);
  let header = parse_message_header(&mut reader)?;

  if header.protocol_version != PROTOCOL_VERSION {
    return Err(Error::new(ErrorKind::InvalidData, "Unsupported protocol version"));
  }

  if packet.len() - 16 < header.message_length as usize {
    return Err(Error::new(ErrorKind::InvalidData, "Received packet is too short"));
  }

  if verify_checksum {
    let checksum = compute_checksum(packet);
    if checksum != header.checksum {
      return Err(Error::new(ErrorKind::InvalidData, "Packet has incorrect checksum"));
    }
  }

  let message_type = parse_message_type(&mut reader)?;

  let payload = parse_message_payload(&mut reader, message_source, message_type)?;

  Ok(Message {
    header,
    message_type,
    payload
  })
}