300 lines
9.3 KiB
Rust
300 lines
9.3 KiB
Rust
extern crate libusb;
|
|
|
|
const FIRE_VENDOR: u16 = 0x09e8;
|
|
const FIRE_PRODUCT: u16 = 0x0043;
|
|
|
|
pub fn get_fires<'a>(context: &libusb::Context) -> Vec<libusb::Device> {
|
|
let devs = context.devices().expect("Failed to get connected devices");
|
|
|
|
devs.iter().filter(|d| {
|
|
if let Ok(desc) = d.device_descriptor() {
|
|
if desc.vendor_id() == FIRE_VENDOR && desc.product_id() == FIRE_PRODUCT {
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
} else {
|
|
false
|
|
}
|
|
}).collect::<Vec<_>>()
|
|
}
|
|
|
|
pub fn claim_fires<'a>(fires: &'a [libusb::Device]) -> Vec<libusb::DeviceHandle<'a>> {
|
|
let mut handles = fires.iter().map(|dev| dev.open().expect("Failed to access a FIRE")).collect::<Vec<_>>();
|
|
handles.iter_mut().for_each(|h| {
|
|
let _ = h.detach_kernel_driver(1); // Allowed to fail due to for example no attached kernel driver
|
|
h.claim_interface(1).expect("Failed to claim a FIRE");
|
|
});
|
|
|
|
handles
|
|
}
|
|
|
|
const INPUT_BLOCKS: usize = 5;
|
|
|
|
pub fn get_input(fires: &[libusb::DeviceHandle], timeout: std::time::Duration) -> Vec<[u8; INPUT_BLOCKS * 4]> {
|
|
let mut res = Vec::with_capacity(fires.len());
|
|
for h in fires.iter() {
|
|
let mut buf = [0u8; INPUT_BLOCKS*4];
|
|
match h.read_bulk(0x81, &mut buf, timeout) {
|
|
Ok(_) => {},
|
|
Err(e) => {
|
|
match e {
|
|
libusb::Error::Timeout => {},
|
|
_ => panic!("Failed to read FIRE {}", e),
|
|
}
|
|
},
|
|
}
|
|
res.push(buf);
|
|
}
|
|
|
|
res
|
|
}
|
|
|
|
const UNKNOWN_EXTRA: usize = 2;
|
|
|
|
pub fn render_pixelbuf(buf: &[u8], width: usize, height: usize, x: usize, y: usize) -> Vec<u8> {
|
|
let write_mode = 0x0e;
|
|
let mut res: Vec<u8> = Vec::new();
|
|
|
|
let out_buf = pix8_to_pix7(&transform_pixel_buffer(buf, width));
|
|
let block_rem = (out_buf.len() - 2) % 3;
|
|
let length = out_buf.len() + (2 - block_rem) + 2 + UNKNOWN_EXTRA;
|
|
|
|
// Insert the header
|
|
let first = out_buf[0];
|
|
res.extend_from_slice(&[0x04,0xf0,0x47,0x7f,0x04,0x43,write_mode,
|
|
(length >> 7) as u8,0x04,(length & 0b111_1111) as u8,
|
|
(y / 8) as u8,((y+height) / 8 - 1) as u8,0x04,x as u8,(x+width-1) as u8, first]);
|
|
|
|
|
|
// And the pixel buffer
|
|
res.extend_from_slice(&[0x0f, out_buf[1], 0x0, 0x0]);
|
|
for (i, v) in out_buf[2..].iter().enumerate() {
|
|
if (i % 3) == 0 {
|
|
res.push(0x04);
|
|
}
|
|
res.push(*v);
|
|
}
|
|
|
|
if block_rem > 0 {
|
|
// There is a partial block left, change the last block
|
|
// to the termination sequence
|
|
|
|
let pos = res.len() - block_rem - 1;
|
|
res[pos] = 0x07;
|
|
for _ in 0..(2-block_rem) {
|
|
res.push(0x00);
|
|
}
|
|
} else {
|
|
// Just add the termination sequence
|
|
res.push(0x07);
|
|
res.push(0x00);
|
|
res.push(0x00);
|
|
}
|
|
res.push(0xf7);
|
|
|
|
res
|
|
}
|
|
|
|
fn reverse_byte(b: u8) -> u8 {
|
|
let mut r = 0u8;
|
|
for s in 0..8 {
|
|
r |= (b >> s & 1) << (7 - s)
|
|
}
|
|
|
|
r
|
|
}
|
|
|
|
struct Bitbuffer<'a> { // NOTE Could be an iterator
|
|
buf: &'a [u8],
|
|
index: usize,
|
|
shift: usize,
|
|
}
|
|
|
|
impl<'a> Bitbuffer<'a> {
|
|
fn new(buf: &'a [u8]) -> Self {
|
|
Self {
|
|
buf,
|
|
index: 0,
|
|
shift: 7,
|
|
}
|
|
}
|
|
|
|
fn pop_bit(&mut self) -> Option<u8> {
|
|
if self.index >= self.buf.len() {
|
|
None
|
|
} else {
|
|
let bit = (self.buf[self.index] >> self.shift) & 0b1;
|
|
if self.shift == 0 {
|
|
self.shift = 7;
|
|
self.index += 1;
|
|
} else {
|
|
self.shift -= 1;
|
|
}
|
|
|
|
Some(bit)
|
|
}
|
|
}
|
|
|
|
fn has_more(&self) -> bool {
|
|
self.index < self.buf.len()
|
|
}
|
|
}
|
|
|
|
fn pix8_to_pix7(buf: &[u8]) -> Vec<u8> {
|
|
let mut res = Vec::with_capacity(buf.len() * 8 / 7);
|
|
let mut bitbuf = Bitbuffer::new(&buf);
|
|
|
|
loop {
|
|
let mut byte = 0;
|
|
for _ in 0..7 {
|
|
byte <<= 1;
|
|
byte |= bitbuf.pop_bit().unwrap_or(0);
|
|
}
|
|
|
|
res.push(byte);
|
|
|
|
if !bitbuf.has_more() {
|
|
break;
|
|
}
|
|
}
|
|
res
|
|
}
|
|
|
|
fn transform_pixel_buffer(buf: &[u8], width: usize) -> Vec<u8> {
|
|
let mut res = Vec::with_capacity(buf.len());
|
|
let dwidth = width / 8;
|
|
|
|
for line in 0..(buf.len() / width) {
|
|
for x_bit in 0..width {
|
|
let mut byte = 0;
|
|
for y in 0..8 {
|
|
let val = *buf.get(line * 8 * dwidth
|
|
+ y * dwidth
|
|
+ x_bit / 8).unwrap_or(&0);
|
|
byte <<= 1;
|
|
byte |= (reverse_byte(val) >> (x_bit % 8)) & 0b1;
|
|
}
|
|
res.push(reverse_byte(byte));
|
|
}
|
|
}
|
|
|
|
res
|
|
}
|
|
|
|
const clear_screen: [u8; 16 * 8] = [
|
|
0x04,0xf0,0x47,0x7f,0x04,0x43,0x08,0x00,
|
|
0x04,0x03,0x00,0x00,0x06,0x00,0xf7,0x00,
|
|
0x04,0xf0,0x47,0x7f,0x04,0x43,0x08,0x00,
|
|
0x04,0x03,0x00,0x00,0x06,0x01,0xf7,0x00,
|
|
0x04,0xf0,0x47,0x7f,0x04,0x43,0x08,0x00,
|
|
0x04,0x03,0x00,0x00,0x06,0x02,0xf7,0x00,
|
|
0x04,0xf0,0x47,0x7f,0x04,0x43,0x08,0x00,
|
|
0x04,0x03,0x00,0x00,0x06,0x03,0xf7,0x00,
|
|
0x04,0xf0,0x47,0x7f,0x04,0x43,0x08,0x00,
|
|
0x04,0x03,0x00,0x00,0x06,0x04,0xf7,0x00,
|
|
0x04,0xf0,0x47,0x7f,0x04,0x43,0x08,0x00,
|
|
0x04,0x03,0x00,0x00,0x06,0x05,0xf7,0x00,
|
|
0x04,0xf0,0x47,0x7f,0x04,0x43,0x08,0x00,
|
|
0x04,0x03,0x00,0x00,0x06,0x06,0xf7,0x00,
|
|
0x04,0xf0,0x47,0x7f,0x04,0x43,0x08,0x00,
|
|
0x04,0x03,0x00,0x00,0x06,0x07,0xf7,0x00
|
|
];
|
|
|
|
pub fn read_pbm(path: &str) -> (usize, usize, Vec<u8>) {
|
|
let data = std::fs::read(path).unwrap();
|
|
let s = String::from_utf8(data).unwrap();
|
|
let mut iter = s.split_whitespace();
|
|
assert_eq!(iter.next(), Some("P1"));
|
|
let w = iter.next().unwrap().parse::<usize>().unwrap();
|
|
let h = iter.next().unwrap().parse::<usize>().unwrap();
|
|
let mut res = Vec::with_capacity(w * h);
|
|
let mut bit = 0;
|
|
let mut byte = 0;
|
|
for e in iter {
|
|
byte <<= 1;
|
|
byte |= if e == "1" { 1 } else { 0 };
|
|
bit += 1;
|
|
if bit == 8 {
|
|
res.push(byte);
|
|
bit = 0;
|
|
}
|
|
}
|
|
assert_eq!(w/8*h, res.len(), "Image data has invalid or incompatible dimensions");
|
|
|
|
(w, h, res)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn byte_reverse_1() {
|
|
assert_eq!(reverse_byte(0b11001010), 0b01010011);
|
|
}
|
|
|
|
#[test]
|
|
fn pix8_to_pix7_1() {
|
|
assert_eq!(&pix8_to_pix7(&[0,255,0,255,0,255,0,255]),
|
|
&[0b0000000, 0b0111111, 0b1100000, 0b0001111,
|
|
0b1111000, 0b0000011, 0b1111110, 0b0000000,
|
|
0b1111111, 0b1000000])
|
|
}
|
|
|
|
#[test]
|
|
fn transform_pixel_buffer_1() {
|
|
assert_eq!(&transform_pixel_buffer(&[0b00000000,
|
|
0b11111111,
|
|
0b10101010,
|
|
0b01010101,
|
|
0b10101010,
|
|
0b11110000,
|
|
0b00001111,
|
|
0b11111111], 8),
|
|
&[0b10110110,0b10101010,0b10110110,0b10101010,
|
|
0b11010110,0b11001010,0b11010110,0b11001010]);
|
|
}
|
|
|
|
#[test]
|
|
fn transform_pixel_buffer_2() {
|
|
assert_eq!(&transform_pixel_buffer(&[0b00000000,0b00000000,
|
|
0b11111111,0b11111111,
|
|
0b10101010,0b10101010,
|
|
0b01010101,0b01010101,
|
|
0b10101010,0b10101010,
|
|
0b11110000,0b11110000,
|
|
0b00001111,0b00001111,
|
|
0b11111111,0b11111111,
|
|
0b00000000,0b00000000,
|
|
0b11111111,0b11111111,
|
|
0b10101010,0b10101010,
|
|
0b01010101,0b01010101,
|
|
0b10101010,0b10101010,
|
|
0b11110000,0b11110000,
|
|
0b00001111,0b00001111,
|
|
0b11111111,0b11111111], 16),
|
|
&[0b10110110,0b10101010,0b10110110,0b10101010,
|
|
0b11010110,0b11001010,0b11010110,0b11001010,
|
|
0b10110110,0b10101010,0b10110110,0b10101010,
|
|
0b11010110,0b11001010,0b11010110,0b11001010,
|
|
0b10110110,0b10101010,0b10110110,0b10101010,
|
|
0b11010110,0b11001010,0b11010110,0b11001010,
|
|
0b10110110,0b10101010,0b10110110,0b10101010,
|
|
0b11010110,0b11001010,0b11010110,0b11001010]);
|
|
}
|
|
|
|
#[test]
|
|
fn bitbuffer_1() {
|
|
let bits = [0b10100101, 0b00001111];
|
|
let mut bitbuf = Bitbuffer::new(&bits);
|
|
for s in 0..8 {
|
|
assert_eq!(bitbuf.pop_bit(), Some((bits[0] >> (7 - s)) & 0b1));
|
|
}
|
|
for s in 0..8 {
|
|
assert_eq!(bitbuf.pop_bit(), Some((bits[1] >> (7 - s)) & 0b1));
|
|
}
|
|
assert_eq!(bitbuf.pop_bit(), None);
|
|
}
|
|
}
|