use core::{fmt, mem};
use crate::{Error, Result};
pub const FIRMWARE_MAGIC_LEN: usize = 3;
pub const FIRMWARE_MAGIC: [u8; FIRMWARE_MAGIC_LEN] = [b'I', b'T', b'L'];
pub const FIRMWARE_MAGIC_NUM: u32 = 0x49544c;
pub const FIRMWARE_HDR_DATA_LEN: usize = 117;
pub const FIRMWARE_HEADER_LEN: usize = 128;
pub const FIRMWARE_RESERVED_LEN: usize = 3;
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FirmwareHeader {
magic: [u8; FIRMWARE_MAGIC_LEN],
_reserved: [u8; FIRMWARE_RESERVED_LEN],
file_code: u8,
ram_len: u32,
data: [u8; FIRMWARE_HDR_DATA_LEN],
}
impl FirmwareHeader {
pub const fn new() -> Self {
Self {
magic: FIRMWARE_MAGIC,
_reserved: [0; FIRMWARE_RESERVED_LEN],
file_code: 0,
ram_len: 0,
data: [0; FIRMWARE_HDR_DATA_LEN],
}
}
pub fn magic(&self) -> u32 {
u32::from_be_bytes([0, self.magic[0], self.magic[1], self.magic[2]])
}
pub fn magic_str(&self) -> &str {
core::str::from_utf8(self.magic.as_ref()).unwrap_or("")
}
pub(crate) fn reserved(&self) -> u32 {
u32::from_be_bytes([0, self._reserved[0], self._reserved[1], self._reserved[2]])
}
pub const fn file_code(&self) -> u8 {
self.file_code
}
pub const fn ram_len(&self) -> u32 {
self.ram_len
}
pub const fn data(&self) -> &[u8] {
&self.data
}
pub fn to_bytes(&self, buf: &mut [u8]) -> Result<()> {
let len = buf.len();
if len < FIRMWARE_HEADER_LEN {
Err(Error::Firmware(format!("invalid header destination buffer length, have: {len}, expected: {FIRMWARE_HEADER_LEN}")))
} else {
let mut idx = 0;
buf[..FIRMWARE_MAGIC_LEN].copy_from_slice(&self.magic);
idx += FIRMWARE_MAGIC_LEN;
let res_end = idx + FIRMWARE_RESERVED_LEN;
buf[idx..res_end].copy_from_slice(&self._reserved);
idx = res_end;
buf[idx] = self.file_code;
idx += mem::size_of::<u8>();
let ram_end = idx + mem::size_of::<u32>();
buf[idx..ram_end].copy_from_slice(self.ram_len.to_be_bytes().as_ref());
idx = ram_end;
let data_end = idx + FIRMWARE_HDR_DATA_LEN;
buf[idx..data_end].copy_from_slice(&self.data);
Ok(())
}
}
}
impl TryFrom<&FirmwareHeader> for [u8; FIRMWARE_HEADER_LEN] {
type Error = Error;
fn try_from(val: &FirmwareHeader) -> Result<Self> {
let mut buf = [0u8; FIRMWARE_HEADER_LEN];
val.to_bytes(&mut buf)?;
Ok(buf)
}
}
impl TryFrom<FirmwareHeader> for [u8; FIRMWARE_HEADER_LEN] {
type Error = Error;
fn try_from(val: FirmwareHeader) -> Result<Self> {
(&val).try_into()
}
}
impl TryFrom<&[u8]> for FirmwareHeader {
type Error = Error;
fn try_from(val: &[u8]) -> Result<Self> {
let len = val.len();
if len < FIRMWARE_HEADER_LEN {
Err(Error::Firmware(format!(
"invalid header length, have: {len}, expected: {FIRMWARE_HEADER_LEN}"
)))
} else if val[..FIRMWARE_MAGIC_LEN] != FIRMWARE_MAGIC {
let bad_magic = &val[..FIRMWARE_MAGIC_LEN];
let magic = &FIRMWARE_MAGIC;
Err(Error::Firmware(format!(
"bad magic bytes, have: {bad_magic:x?}, expected: {magic:x?}"
)))
} else {
let mut idx = FIRMWARE_MAGIC_LEN;
let magic = FIRMWARE_MAGIC;
let res_end = idx.saturating_add(FIRMWARE_RESERVED_LEN);
let _reserved: [u8; FIRMWARE_RESERVED_LEN] = val[idx..res_end]
.try_into()
.map_err(|err| Error::Firmware(format!("invalid reserved data: {err}")))?;
idx = res_end;
let file_code = val[idx];
idx += mem::size_of::<u8>();
let ram_len = u32::from_be_bytes([val[idx], val[idx + 1], val[idx + 2], val[idx + 3]]);
idx += mem::size_of::<u32>();
let data_end = idx + FIRMWARE_HDR_DATA_LEN;
let data: [u8; FIRMWARE_HDR_DATA_LEN] = val[idx..data_end]
.try_into()
.map_err(|err| Error::Firmware(format!("invalid firmware data: {err}")))?;
Ok(Self {
magic,
_reserved,
file_code,
ram_len,
data,
})
}
}
}
impl<const N: usize> TryFrom<&[u8; N]> for FirmwareHeader {
type Error = Error;
fn try_from(val: &[u8; N]) -> Result<Self> {
val.as_ref().try_into()
}
}
impl<const N: usize> TryFrom<[u8; N]> for FirmwareHeader {
type Error = Error;
fn try_from(val: [u8; N]) -> Result<Self> {
val.as_ref().try_into()
}
}
impl fmt::Display for FirmwareHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let magic = self.magic_str();
let reserved = self.reserved();
let file_code = self.file_code();
let ram_len = self.ram_len();
write!(f, "{{")?;
write!(f, r#""magic": "{magic}", "#)?;
write!(f, r#""reserved": {reserved}, "#)?;
write!(f, r#""file_code": {file_code}, "#)?;
write!(f, r#""ram_len": {ram_len}, "#)?;
write!(f, r#""data": ["#)?;
for (i, d) in self.data().iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "{d}")?;
}
write!(f, "]}}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[rustfmt::skip]
fn test_header_parse() -> Result<()> {
let hdr_buf = [
b'I', b'T', b'L',
0, 0, 0,
0x03,
0x01, 0x02, 0x03, 0x04,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0,
];
let exp_hdr = FirmwareHeader {
magic: FIRMWARE_MAGIC,
_reserved: [0u8; FIRMWARE_RESERVED_LEN],
file_code: 0x03,
ram_len: 0x01020304,
data: [0u8; FIRMWARE_HDR_DATA_LEN],
};
assert_eq!(FirmwareHeader::try_from(&hdr_buf)?, exp_hdr);
assert_eq!(<[u8; FIRMWARE_HEADER_LEN]>::try_from(&exp_hdr)?, hdr_buf);
Ok(())
}
#[test]
#[rustfmt::skip]
fn test_header_bad_magic() -> Result<()> {
let hdr_buf = [
b'b', b'4', b'd',
0, 0, 0,
0x03,
0x01, 0x02, 0x03, 0x04,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0,
];
assert!(FirmwareHeader::try_from(&hdr_buf).is_err());
Ok(())
}
#[test]
#[rustfmt::skip]
fn test_header_short_buf() -> Result<()> {
let mut hdr_buf = [
b'b', b'4', b'd',
0, 0, 0,
0x03,
0x01, 0x02, 0x03, 0x04,
];
assert!(FirmwareHeader::try_from(&hdr_buf).is_err());
assert!(FirmwareHeader::new().to_bytes(&mut hdr_buf).is_err());
Ok(())
}
}