use std::net::Ipv4Addr;
use crate::wire::{
BayFeatures, BayStatus, DeviceFeature, DeviceUid, Opcode, BAY_CONFIG_SIZE, V2IP_PORT_ANC,
V2IP_PORT_AUDIO, V2IP_PORT_VIDEO,
};
pub(crate) fn poisoned(n: usize) -> Vec<u8> {
(0..n).map(|i| 0xA5 ^ (i as u8)).collect()
}
pub(crate) fn uid_n(n: u8) -> DeviceUid {
let mut bytes = [0u8; 16];
bytes[0] = n;
bytes[15] = 0xAA;
DeviceUid::from_array(bytes)
}
pub(crate) fn field(dst: &mut [u8], at: usize, len: usize, value: &str) {
let bytes = value.as_bytes();
let taken = bytes.get(..len).unwrap_or(bytes);
dst[at..at + taken.len()].copy_from_slice(taken);
dst[at + taken.len()..at + len].fill(0);
}
pub(crate) fn fixed_str(dst: &mut Vec<u8>, value: &str, size: usize) {
let bytes = value.as_bytes();
let taken = bytes.get(..size).unwrap_or(bytes);
dst.extend_from_slice(taken);
dst.resize(dst.len() + (size - taken.len()), 0);
}
pub(crate) fn hello_payload(
protocol: u16,
name: &str,
serial: &str,
version: &str,
features: DeviceFeature,
) -> Vec<u8> {
let mut p = Vec::with_capacity(54);
p.extend_from_slice(&protocol.to_le_bytes());
fixed_str(&mut p, name, 16);
fixed_str(&mut p, serial, 16);
fixed_str(&mut p, version, 16);
p.extend_from_slice(&features.bits().to_le_bytes());
p
}
pub(crate) fn bay_config_rec(
port: u8,
mode: u8,
bay: u8,
name: &str,
user: &str,
status: BayStatus,
features: BayFeatures,
) -> Vec<u8> {
let mut p = poisoned(BAY_CONFIG_SIZE);
p[0] = port;
p[1] = mode;
p[2] = bay;
field(&mut p, 5, 16, name);
field(&mut p, 21, 16, user);
p[53..57].copy_from_slice(&status.bits().to_le_bytes());
p[57..61].copy_from_slice(&features.bits().to_le_bytes());
p
}
pub(crate) fn stream_rec(
uid: DeviceUid,
video: &str,
audio: &str,
anc: &str,
port: u16,
) -> Vec<u8> {
let mut rec = poisoned(40);
rec[0..16].copy_from_slice(uid.as_bytes());
for (off, ip) in [(16, video), (24, audio), (32, anc)] {
let addr: Ipv4Addr = ip.parse().unwrap_or(Ipv4Addr::UNSPECIFIED);
rec[off..off + 4].copy_from_slice(&addr.octets());
rec[off + 4..off + 6].copy_from_slice(&port.to_le_bytes());
}
rec
}
#[derive(Default)]
pub(crate) struct Cfg {
pub(crate) uid: DeviceUid,
pub(crate) video_ip: &'static str,
pub(crate) audio_ip: &'static str,
pub(crate) anc_ip: &'static str,
pub(crate) arc_ip: &'static str,
pub(crate) video_port: u16,
pub(crate) audio_port: u16,
pub(crate) anc_port: u16,
pub(crate) arc_port: u16,
pub(crate) rate: u8,
pub(crate) dscp_video: u8,
pub(crate) dscp_audio: u8,
pub(crate) dscp_anc: u8,
pub(crate) mode: u16,
pub(crate) refresh: u16,
pub(crate) flags: u8,
}
impl Cfg {
pub(crate) fn addresses(uid: DeviceUid, base: &'static str) -> Self {
Self {
uid,
video_ip: base,
video_port: V2IP_PORT_VIDEO,
audio_ip: base,
audio_port: V2IP_PORT_AUDIO,
anc_ip: base,
anc_port: V2IP_PORT_ANC,
..Self::default()
}
}
pub(crate) fn bytes(&self) -> Vec<u8> {
let mut p = poisoned(88);
p[0..16].copy_from_slice(self.uid.as_bytes());
let mut put = |off: usize, ip: &str, port: u16| {
if ip.is_empty() {
return;
}
let addr: Ipv4Addr = ip.parse().unwrap_or(Ipv4Addr::UNSPECIFIED);
p[off..off + 4].copy_from_slice(&addr.octets());
p[off + 4..off + 6].copy_from_slice(&port.to_le_bytes());
};
put(16, self.video_ip, self.video_port);
put(24, self.audio_ip, self.audio_port);
put(32, self.anc_ip, self.anc_port);
put(48, self.arc_ip, self.arc_port);
p[40] = self.rate;
p[41] = self.dscp_video;
p[42] = self.dscp_audio;
p[43] = self.dscp_anc;
p[56..58].copy_from_slice(&self.mode.to_le_bytes());
p[58..60].copy_from_slice(&self.refresh.to_le_bytes());
p[60] = self.flags;
p
}
}
pub(crate) fn datagram(sender: DeviceUid, op: Opcode, protocol: u16, payload: &[u8]) -> Vec<u8> {
let mut data = Vec::with_capacity(24 + payload.len());
data.extend_from_slice(b"P8");
data.extend_from_slice(&protocol.to_le_bytes());
data.extend_from_slice(sender.as_bytes());
data.extend_from_slice(&op.0.to_le_bytes());
let declared = u16::try_from(payload.len()).unwrap_or(u16::MAX);
data.extend_from_slice(&declared.to_le_bytes());
data.extend_from_slice(payload);
data
}