1use crate::prelude::*;
2use bitflags::bitflags;
3
4const STATUS_ID: u8 = 0x20;
5const READ_MEMORY_ID: u8 = 0x21;
6const ACKNOWLEDGE_ID: u8 = 0x22;
7
8bitflags! {
9 #[derive(Debug, Clone, Copy)]
10 pub struct StatusFlags: u8 {
11 const BATTERY_LOW = 0b0000_0001;
12 const EXTENSION_CONTROLLER_CONNECTED = 0b0000_0010;
13 const SPEAKER_ENABLED = 0b0000_0100;
14 const IR_CAMERA_ENABLED = 0b0000_1000;
15 const LED_1 = 0b0001_0000;
16 const LED_2 = 0b0010_0000;
17 const LED_3 = 0b0100_0000;
18 const LED_4 = 0b1000_0000;
19 }
20}
21
22bitflags! {
23 #[derive(Debug, Clone, Copy)]
24 pub struct ButtonData: u16 {
25 const LEFT = 1 << 0;
26 const RIGHT = 1 << 1;
27 const DOWN = 1 << 2;
28 const UP = 1 << 3;
29 const PLUS = 1 << 4;
30
31 const TWO = 1 << 8;
32 const ONE = 1 << 9;
33 const B = 1 << 10;
34 const A = 1 << 11;
35 const MINUS = 1 << 12;
36
37 const HOME = 1 << 15;
38 }
39}
40
41#[repr(C, packed)]
42#[derive(Debug)]
43pub struct StatusData {
44 buttons: ButtonData,
45 flags: StatusFlags,
46 _reserved: [u8; 2],
47 battery_level: u8,
48}
49
50impl StatusData {
51 #[must_use]
53 pub const fn buttons(&self) -> ButtonData {
54 self.buttons
55 }
56
57 #[must_use]
59 pub const fn flags(&self) -> StatusFlags {
60 self.flags
61 }
62
63 #[must_use]
65 pub const fn battery_level(&self) -> u8 {
66 self.battery_level
67 }
68}
69
70#[repr(C, packed)]
71#[derive(Debug)]
72pub struct MemoryData {
73 buttons: ButtonData,
74 size_error_flags: u8,
75 address: [u8; 2],
76 pub data: [u8; 16],
77}
78
79impl MemoryData {
80 #[must_use]
82 pub const fn buttons(&self) -> ButtonData {
83 self.buttons
84 }
85
86 #[must_use]
88 pub const fn size(&self) -> u8 {
89 (self.size_error_flags >> 4) + 1
90 }
91
92 #[must_use]
99 pub const fn error_flag(&self) -> u8 {
100 self.size_error_flags & 0x0F
101 }
102
103 #[must_use]
105 pub const fn address_offset(&self) -> u16 {
106 u16::from_be_bytes(self.address)
107 }
108}
109
110#[repr(C, packed)]
111#[derive(Debug)]
112pub struct AcknowledgeData {
113 buttons: ButtonData,
114 report_number: u8,
115 error_code: u8,
116}
117
118impl AcknowledgeData {
119 #[must_use]
121 pub const fn buttons(&self) -> ButtonData {
122 self.buttons
123 }
124
125 #[must_use]
127 pub const fn report_number(&self) -> u8 {
128 self.report_number
129 }
130
131 #[must_use]
133 pub const fn error_code(&self) -> u8 {
134 self.error_code
135 }
136}
137
138#[repr(C, packed)]
139#[derive(Debug)]
140pub struct WiimoteData {
141 pub data: [u8; 21],
142}
143
144impl WiimoteData {
145 #[must_use]
149 pub const fn buttons(&self) -> ButtonData {
150 let bits = u16::from_le_bytes([self.data[0], self.data[1]]);
151 ButtonData::from_bits_retain(bits)
152 }
153}
154
155#[derive(Debug)]
157pub enum InputReport {
158 StatusInformation(StatusData),
165 ReadMemory(MemoryData),
171 Acknowledge(AcknowledgeData),
177 DataReport(u8, WiimoteData),
185}
186
187macro_rules! transmute_data {
188 ($value:expr, $type:ident) => {{
189 const DATA_SIZE: usize = std::mem::size_of::<$type>();
190 if $value.len() < DATA_SIZE {
191 return Err(WiimoteDeviceError::InvalidData.into());
192 }
193 let mut slice = [0u8; DATA_SIZE];
194 slice.copy_from_slice(&$value[1..=DATA_SIZE]);
195
196 unsafe { std::mem::transmute::<[u8; DATA_SIZE], $type>(slice) }
197 }};
198}
199
200impl InputReport {
201 fn from_status_information(value: &[u8]) -> WiimoteResult<Self> {
202 let data = transmute_data!(value, StatusData);
203 Ok(Self::StatusInformation(data))
204 }
205
206 fn from_read_memory_data(value: &[u8]) -> WiimoteResult<Self> {
207 let data = transmute_data!(value, MemoryData);
208 Ok(Self::ReadMemory(data))
209 }
210
211 fn from_acknowledge(value: &[u8]) -> WiimoteResult<Self> {
212 let data = transmute_data!(value, AcknowledgeData);
213 Ok(Self::Acknowledge(data))
214 }
215
216 fn from_data_report(value: &[u8]) -> Self {
217 const DATA_SIZE: usize = 21;
218 let mut data = [0u8; DATA_SIZE];
219 let bytes_to_copy = usize::min(value.len() - 1, DATA_SIZE);
220 data[..bytes_to_copy].copy_from_slice(&value[1..=bytes_to_copy]);
221
222 Self::DataReport(value[0], WiimoteData { data })
223 }
224}
225
226impl TryFrom<&[u8; WIIMOTE_DEFAULT_REPORT_BUFFER_SIZE]> for InputReport {
227 type Error = WiimoteError;
228
229 fn try_from(value: &[u8; WIIMOTE_DEFAULT_REPORT_BUFFER_SIZE]) -> Result<Self, Self::Error> {
230 let slice_without_length: &[u8] = value.as_slice();
231 Self::try_from(slice_without_length)
232 }
233}
234
235impl TryFrom<&[u8]> for InputReport {
236 type Error = WiimoteError;
237
238 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
239 if value.is_empty() {
240 return Err(WiimoteDeviceError::MissingData.into());
241 }
242 match value[0] {
243 STATUS_ID => Self::from_status_information(value),
244 READ_MEMORY_ID => Self::from_read_memory_data(value),
245 ACKNOWLEDGE_ID => Self::from_acknowledge(value),
246 0x30..=0x3F => Ok(Self::from_data_report(value)),
247 _ => Err(WiimoteDeviceError::InvalidData.into()),
248 }
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 #[test]
257 fn test_status_report() {
258 let mut data = [0u8; WIIMOTE_DEFAULT_REPORT_BUFFER_SIZE];
259 data[0] = 0x20;
260 data[1] = 0b0001_0100; data[2] = 0b0000_0100; data[3] = 0b0010_0101; data[6] = 24; let report = InputReport::try_from(&data).unwrap();
267
268 assert!(matches!(report, InputReport::StatusInformation(_)));
269 if let InputReport::StatusInformation(data) = report {
270 assert_eq!(
271 data.buttons().bits(),
272 ButtonData::DOWN
273 .union(ButtonData::PLUS)
274 .union(ButtonData::B)
275 .bits()
276 );
277 assert_eq!(
278 data.flags().bits(),
279 StatusFlags::BATTERY_LOW
280 .union(StatusFlags::SPEAKER_ENABLED)
281 .union(StatusFlags::LED_2)
282 .bits()
283 );
284 assert_eq!(data.battery_level(), 24);
285 }
286 }
287
288 #[test]
289 fn test_read_memory_report() {
290 let mut data = [0u8; WIIMOTE_DEFAULT_REPORT_BUFFER_SIZE];
291 data[0] = 0x21;
292 data[1] = 0b0000_0000; data[2] = 0b1000_0000; data[3] = 0xF7; data[4] = 0x12; data[5] = 0xAB; data[6..22].copy_from_slice(b"1234567890123456"); let report = InputReport::try_from(&data).unwrap();
300
301 assert!(matches!(report, InputReport::ReadMemory(_)));
302 if let InputReport::ReadMemory(data) = report {
303 assert_eq!(data.buttons().bits(), ButtonData::HOME.bits());
304 assert_eq!(data.size(), 16);
305 assert_eq!(data.error_flag(), 7);
306 assert_eq!(data.address_offset(), 0x12AB);
307 assert_eq!(data.data, *b"1234567890123456");
308 }
309 }
310
311 #[test]
312 fn test_acknowledge_report() {
313 let data: &[u8] = &[
314 0x22,
315 0b0000_0000, 0b0000_0000, 0x12, 0xAB, ];
320
321 let report = InputReport::try_from(data).unwrap();
322
323 assert!(matches!(report, InputReport::Acknowledge(_)));
324 if let InputReport::Acknowledge(data) = report {
325 assert_eq!(data.buttons().bits(), 0);
326 assert_eq!(data.report_number(), 0x12);
327 assert_eq!(data.error_code(), 0xAB);
328 }
329 }
330
331 #[test]
332 fn test_buttons_mode_0x30() {
333 let data: &[u8] = &[
334 0x30,
335 0b0000_0001, 0b0000_0010, ];
338
339 let report = InputReport::try_from(data).unwrap();
340
341 assert!(matches!(report, InputReport::DataReport(0x30, _)));
342 if let InputReport::DataReport(_, data) = report {
343 assert_eq!(
344 data.buttons().bits(),
345 ButtonData::LEFT.union(ButtonData::ONE).bits()
346 );
347 }
348 }
349}