diff --git a/src/backends/laverita_v2.rs b/src/backends/laverita_v2.rs index a50fc49..0878f81 100644 --- a/src/backends/laverita_v2.rs +++ b/src/backends/laverita_v2.rs @@ -47,6 +47,7 @@ use anyhow::Result; #[cfg(any(chuni, chusanapp))] use rusb::{DeviceHandle, UsbContext}; +use crate::TIMEOUT; use super::ReadType; pub const DEVICE_VID: u16 = 0x1973; @@ -54,8 +55,22 @@ pub const DEVICE_PID: u16 = 0x2001; pub const DEVICE_INTERFACE: u8 = 0; pub const READ_TYPE: ReadType = ReadType::Interrupt; pub const READ_ENDPOINT: u8 = 0x81; -pub const INPUT_MEMORY_SIZE: usize = 35; -pub const OUTPUT_MEMORY_SIZE: usize = 63; +pub const INPUT_MEMORY_SIZE: usize = std::mem::size_of::(); +pub const OUTPUT_MEMORY_SIZE: usize = std::mem::size_of::(); + +#[repr(C)] +struct LaveritaV2Input { + pub report_id: u8, + pub ir: u8, + pub buttons: u8, + pub slider_pressure: [u8; 32], +} + +#[repr(C)] +struct LaveritaV2Output { + pub report_id: u8, + pub slider_led: [u8; 62], +} /// Poll JVS input. /// @@ -69,17 +84,20 @@ pub const OUTPUT_MEMORY_SIZE: usize = 63; #[cfg(any(chuni, amdaemon))] #[inline(always)] pub fn jvs_poll(input: &[u8]) -> (u8, u8) { - let mixed_beams = input[1] & 0x3F; + let input = input.as_ptr() as *const LaveritaV2Input; + let mixed_beams = unsafe { (*input).ir & 0x3F }; let beams = ((mixed_beams & 0xAA) >> 1) | ((mixed_beams & 0x55) << 1); - (input[2] & 3, beams) + (unsafe { (*input).buttons & 3 }, beams) } /// Checks the current state of the coin button (if there is one). #[cfg(any(chuni, amdaemon))] #[inline(always)] pub fn is_coin_button_pressed(input: &[u8]) -> bool { - (input[2] & 4) != 0 + let input = input.as_ptr() as *const LaveritaV2Input; + + unsafe { ((*input).buttons & 4) != 0 } } /// Reads slider pressure information from USB input. @@ -103,10 +121,11 @@ pub fn is_coin_button_pressed(input: &[u8]) -> bool { #[cfg(any(chuni, chusanapp))] #[inline(always)] pub fn read_pressure_data(input: &[u8]) -> [u8; 32] { + let input = input.as_ptr() as *const LaveritaV2Input; let mut pressure = [0u8; 32]; for i in 0..32 { - pressure[i] = input[3 + 31 - i]; + pressure[i] = unsafe { (*input).slider_pressure[31 - i] }; } pressure @@ -117,7 +136,11 @@ pub fn read_pressure_data(input: &[u8]) -> [u8; 32] { #[cfg(any(chuni, chusanapp))] #[inline(always)] pub fn init_output_buffer(output: &mut [u8]) { - output[0] = 0; + let output = output.as_mut_ptr() as *mut LaveritaV2Output; + + unsafe { + (*output).report_id = 0; + } } /// Update the RGB lighting on the slider. A slice `rgb` is provided, alternating @@ -129,13 +152,19 @@ pub fn set_slider_leds( output: &mut [u8], brg: &[u8], ) -> Result<()> { - use crate::TIMEOUT; + unsafe { + let output = output.as_mut_ptr() as *mut LaveritaV2Output; - for (buf_chunk, state_chunk) in output[1..].chunks_mut(2).take(31).zip(brg.chunks(3)) { - buf_chunk[0] = (state_chunk[0] << 3 & 0xE0) | (state_chunk[2] >> 3); - buf_chunk[1] = (state_chunk[1] & 0xF8) | (state_chunk[0] >> 5); + for (buf_chunk, state_chunk) in (*output).slider_led + .chunks_mut(2) + .take(31) + .zip(brg.chunks(3)) + { + buf_chunk[0] = (state_chunk[0] << 3 & 0xE0) | (state_chunk[2] >> 3); + buf_chunk[1] = (state_chunk[1] & 0xF8) | (state_chunk[0] >> 5); + } } - + device.write_interrupt(0x02, output, TIMEOUT)?; Ok(()) diff --git a/src/backends/laverita_v3.rs b/src/backends/laverita_v3.rs index 3dabd93..8e44683 100644 --- a/src/backends/laverita_v3.rs +++ b/src/backends/laverita_v3.rs @@ -31,6 +31,7 @@ use anyhow::Result; #[cfg(any(chuni, chusanapp))] use rusb::{DeviceHandle, UsbContext}; +use crate::TIMEOUT; use super::ReadType; pub const DEVICE_VID: u16 = 0x0518; @@ -38,8 +39,25 @@ pub const DEVICE_PID: u16 = 0x2022; pub const DEVICE_INTERFACE: u8 = 3; pub const READ_TYPE: ReadType = ReadType::Interrupt; pub const READ_ENDPOINT: u8 = 0x83; -pub const INPUT_MEMORY_SIZE: usize = 34; -pub const OUTPUT_MEMORY_SIZE: usize = 122; +pub const INPUT_MEMORY_SIZE: usize = std::mem::size_of::(); +pub const OUTPUT_MEMORY_SIZE: usize = std::mem::size_of::(); + +#[repr(C)] +struct LaveritaV3Input { + pub ir_buttons: u8, + pub magic: u8, + pub slider_pressure: [u8; 32], +} + +#[repr(C)] +struct LaveritaV3Output { + pub report_id_zero: u8, + pub slider_led_zero: [u8; 60], + + pub report_id_one: u8, + pub slider_led_one: [u8; 33], + pub padding: [u8; 27], +} /// Poll JVS input. /// @@ -54,10 +72,11 @@ pub const OUTPUT_MEMORY_SIZE: usize = 122; #[inline(always)] pub fn jvs_poll(input: &[u8]) -> (u8, u8) { // Taken from laverita_v2 - let mixed_beams = input[0] & 0x3F; + let input = input.as_ptr() as *const LaveritaV3Input; + let mixed_beams = unsafe { (*input).ir_buttons & 0x3F }; let beams = ((mixed_beams & 0xAA) >> 1) | ((mixed_beams & 0x55) << 1); - (input[0] >> 6, beams) + (unsafe { (*input).ir_buttons >> 6 }, beams) } /// Checks the current state of the coin button (if there is one). @@ -90,10 +109,13 @@ pub fn is_coin_button_pressed(_input: &[u8]) -> bool { #[cfg(any(chuni, chusanapp))] #[inline(always)] pub fn read_pressure_data(input: &[u8]) -> [u8; 32] { + let input = input.as_ptr() as *const LaveritaV3Input; let mut pressure = [0u8; 32]; - for (i, p) in input[2..34].iter().enumerate() { - pressure[if i % 2 == 0 { 30 - i } else { 32 - i }] = *p; + unsafe { + for (i, p) in (*input).slider_pressure.iter().enumerate() { + pressure[if i % 2 == 0 { 30 - i } else { 32 - i }] = *p; + } } pressure @@ -104,9 +126,13 @@ pub fn read_pressure_data(input: &[u8]) -> [u8; 32] { #[cfg(any(chuni, chusanapp))] #[inline(always)] pub fn init_output_buffer(output: &mut [u8]) { + let output = output.as_mut_ptr() as *mut LaveritaV3Output; + // Packet indices - output[0] = 0; - output[61] = 1; + unsafe { + (*output).report_id_zero = 0; + (*output).report_id_one = 1; + } } /// Update the RGB lighting on the slider. A slice `rgb` is provided, alternating @@ -118,21 +144,26 @@ pub fn set_slider_leds( output: &mut [u8], brg: &[u8], ) -> Result<()> { - use crate::TIMEOUT; + let output_data = output.as_mut_ptr() as *mut LaveritaV3Output; - for (output_chunk, brg_chunk) in output[1..61].chunks_mut(3).zip(brg.chunks(3).take(20)) { - output_chunk[0] = brg_chunk[1]; // R - output_chunk[1] = brg_chunk[2]; // G - output_chunk[2] = brg_chunk[0]; // B - } - - for (output_chunk, brg_chunk) in output[62..95] - .chunks_mut(3) - .zip(brg.chunks(3).skip(20).take(11)) - { - output_chunk[0] = brg_chunk[1]; // R - output_chunk[1] = brg_chunk[2]; // G - output_chunk[2] = brg_chunk[0]; // B + unsafe { + for (output_chunk, brg_chunk) in (*output_data).slider_led_zero + .chunks_mut(3) + .zip(brg.chunks(3).take(20)) + { + output_chunk[0] = brg_chunk[1]; // R + output_chunk[1] = brg_chunk[2]; // G + output_chunk[2] = brg_chunk[0]; // B + } + + for (output_chunk, brg_chunk) in (*output_data).slider_led_one + .chunks_mut(3) + .zip(brg.chunks(3).skip(20).take(11)) + { + output_chunk[0] = brg_chunk[1]; // R + output_chunk[1] = brg_chunk[2]; // G + output_chunk[2] = brg_chunk[0]; // B + } } device.write_interrupt(0x03, &output[0..61], TIMEOUT)?; diff --git a/src/backends/tasoller_plus.rs b/src/backends/tasoller_plus.rs index 8d77271..dc13a62 100644 --- a/src/backends/tasoller_plus.rs +++ b/src/backends/tasoller_plus.rs @@ -35,8 +35,24 @@ pub const DEVICE_PID: u16 = 0x1231; pub const DEVICE_INTERFACE: u8 = 0; pub const READ_TYPE: ReadType = ReadType::Interrupt; pub const READ_ENDPOINT: u8 = 0x84; -pub const INPUT_MEMORY_SIZE: usize = 36; -pub const OUTPUT_MEMORY_SIZE: usize = 114; +pub const INPUT_MEMORY_SIZE: usize = std::mem::size_of::(); +pub const OUTPUT_MEMORY_SIZE: usize = std::mem::size_of::(); + +#[repr(C)] +pub struct TasollerPlusInput { + pub magic: [u8; 3], + pub ir_buttons: u8, + pub slider_pressure: [u8; 32], +} + +#[repr(C)] +pub struct TasollerPlusOutput { + pub magic: [u8; 2], + pub protocol_version: u8, + pub slider_led: [u8; 93], + pub left_air_tower_led: [u8; 9], + pub right_air_tower_led: [u8; 9], +} /// Poll JVS input. /// @@ -50,12 +66,13 @@ pub const OUTPUT_MEMORY_SIZE: usize = 114; #[cfg(any(chuni, amdaemon))] #[inline(always)] pub fn jvs_poll(input: &[u8]) -> (u8, u8) { + let input = input.as_ptr() as *const TasollerPlusInput; // The order of the bits from MSB to LSB is // FN1 FN2 IR1 IR2 IR3 IR4 IR5 IR6 // so when reversed, it becomes // IR6 IR5 IR4 IR3 IR2 IR1 FN2 FN1 // which is basically what segatools expect. - let reversed = input[3].reverse_bits(); + let reversed = unsafe { (*input).ir_buttons.reverse_bits() }; let opbtn = reversed & 3; let beams = reversed >> 2; @@ -91,9 +108,10 @@ pub fn is_coin_button_pressed(_input: &[u8]) -> bool { #[cfg(any(chuni, chusanapp))] #[inline(always)] pub fn read_pressure_data(input: &[u8]) -> [u8; 32] { + let input = input.as_ptr() as *const TasollerPlusInput; let mut pressure = [0u8; 32]; - pressure.copy_from_slice(&input[4..36]); + pressure.copy_from_slice(unsafe { (*input).slider_pressure.as_slice() }); pressure } @@ -103,9 +121,13 @@ pub fn read_pressure_data(input: &[u8]) -> [u8; 32] { #[cfg(any(chuni, chusanapp))] #[inline(always)] pub fn init_output_buffer(output: &mut [u8]) { - output[0] = 0x44; - output[1] = 0x4C; - output[2] = 0x02; + let output = output.as_mut_ptr() as *mut TasollerPlusOutput; + + unsafe { + (*output).magic[0] = 0x44; + (*output).magic[1] = 0x4C; + (*output).protocol_version = 0x02; + } } /// Update the RGB lighting on the slider. A slice `rgb` is provided, alternating @@ -117,7 +139,13 @@ pub fn set_slider_leds( output: &mut [u8], brg: &[u8], ) -> Result<()> { - for (output_chunk, brg_chunk) in output[3..96].chunks_mut(3).take(31).zip(brg.chunks(3)) { + let output_data = output.as_mut_ptr() as *mut TasollerPlusOutput; + + for (output_chunk, brg_chunk) in unsafe { (*output_data).slider_led } + .chunks_mut(3) + .take(31) + .zip(brg.chunks(3)) + { output_chunk[0] = brg_chunk[1]; // R output_chunk[1] = brg_chunk[2]; // G output_chunk[2] = brg_chunk[0]; // B @@ -150,10 +178,16 @@ pub fn set_led_colors( return Ok(()); } - let output_start = if board == 0 { 0x60 } else { 0x69 }; + let output_data = output.as_mut_ptr() as *mut TasollerPlusOutput; let air_rgb_start = if board == 0 { 0x96 } else { 0xB4 }; - output[output_start..output_start + 9].copy_from_slice(&rgb[air_rgb_start..air_rgb_start + 9]); + unsafe { + if board == 0 { + (*output_data).left_air_tower_led.copy_from_slice(&rgb[air_rgb_start..air_rgb_start + 9]); + } else { + (*output_data).right_air_tower_led.copy_from_slice(&rgb[air_rgb_start..air_rgb_start + 9]); + } + } device.write_bulk(0x03, output, TIMEOUT)?; diff --git a/src/backends/tasoller_v2.rs b/src/backends/tasoller_v2.rs index 3b857c6..3f7e4a6 100644 --- a/src/backends/tasoller_v2.rs +++ b/src/backends/tasoller_v2.rs @@ -34,21 +34,39 @@ pub const DEVICE_PID: u16 = 0x2333; pub const DEVICE_INTERFACE: u8 = 0; pub const READ_TYPE: ReadType = ReadType::Interrupt; pub const READ_ENDPOINT: u8 = 0x84; -pub const INPUT_MEMORY_SIZE: usize = 36; -pub const OUTPUT_MEMORY_SIZE: usize = 240; +pub const INPUT_MEMORY_SIZE: usize = std::mem::size_of::(); +pub const OUTPUT_MEMORY_SIZE: usize = std::mem::size_of::(); + +#[repr(C)] +struct TasollerV2Input { + pub magic: [u8; 3], + pub ir_buttons: u8, + pub slider_pressure: [u8; 32], +} + +#[repr(C)] +struct TasollerV2Output { + pub magic: [u8; 3], + pub slider_led: [u8; 93], + pub left_air_tower_led: [u8; 72], + pub right_air_tower_led: [u8; 72], +} #[cfg(any(chuni, amdaemon))] #[inline(always)] pub fn jvs_poll(input: &[u8]) -> (u8, u8) { + let input = input.as_ptr() as *const TasollerV2Input; + let ir_buttons = unsafe { (*input).ir_buttons }; + // Drop the IR bits from the input... - let opbtn = input[3] >> 6; + let opbtn = ir_buttons >> 6; // Swap bit position of FN1 and FN2 so that FN1 maps to Test (bit 0) // and FN2 maps to service (bit 1) let opbtn = (opbtn & 1) << 1 | (opbtn & 2) >> 1; // Mask for last 6 bits to get the IR beams - let beams = input[3] & 63; + let beams = ir_buttons & 63; (opbtn, beams) } @@ -63,9 +81,12 @@ pub fn is_coin_button_pressed(_input: &[u8]) -> bool { #[cfg(any(chuni, chusanapp))] #[inline(always)] pub fn read_pressure_data(input: &[u8]) -> [u8; 32] { + let input = input.as_ptr() as *const TasollerV2Input; + let slider_pressure = unsafe { (*input).slider_pressure.as_slice() }; + let mut pressure = [0u8; 32]; - for (i, p) in input.iter().skip(4).take(32).enumerate() { + for (i, p) in slider_pressure.iter().enumerate() { pressure[if i % 2 == 0 { 30 - i } else { 32 - i }] = *p; } @@ -75,9 +96,13 @@ pub fn read_pressure_data(input: &[u8]) -> [u8; 32] { #[cfg(any(chuni, chusanapp))] #[inline(always)] pub fn init_output_buffer(output: &mut [u8]) { - output[0] = 0x42; - output[1] = 0x4C; - output[2] = 0; + let output = output.as_mut_ptr() as *mut TasollerV2Output; + + unsafe { + (*output).magic[0] = 0x42; + (*output).magic[1] = 0x4C; + (*output).magic[2] = 0; + } } #[cfg(any(chuni, chusanapp))] @@ -87,7 +112,13 @@ pub fn set_slider_leds( output: &mut [u8], brg: &[u8], ) -> Result<()> { - for (output_chunk, brg_chunk) in output[3..96].chunks_mut(3).take(31).zip(brg.chunks(3)) { + let output_data = output.as_mut_ptr() as *mut TasollerV2Output; + + for (output_chunk, brg_chunk) in unsafe { (*output_data).slider_led } + .chunks_mut(3) + .take(31) + .zip(brg.chunks(3)) + { output_chunk[0] = brg_chunk[2]; // G output_chunk[1] = brg_chunk[1]; // R output_chunk[2] = brg_chunk[0]; // B @@ -110,13 +141,21 @@ pub fn set_led_colors( return Ok(()); } - let air_rgb = if board == 0 { 0x96 } else { 0xB4 }; - let output_start = if board == 0 { 96 } else { 168 }; + let output_data = output.as_mut_ptr() as *mut TasollerV2Output; + let air_rgb_start = if board == 0 { 0x96 } else { 0xB4 }; + let mut air_tower_buf = unsafe { + if board == 0 { + (*output_data).left_air_tower_led + } else { + (*output_data).right_air_tower_led + } + }; + - for (buf_chunk, state_chunk) in output[output_start..output_start + 72] + for (buf_chunk, state_chunk) in air_tower_buf .chunks_mut(3) .take(24) - .zip(rgb[air_rgb..air_rgb + 9].chunks(3).cycle().take(24)) + .zip(rgb[air_rgb_start..air_rgb_start + 9].chunks(3).cycle().take(24)) { buf_chunk[0] = state_chunk[1]; // R buf_chunk[1] = state_chunk[0]; // B