use std::io::{Read, Seek};
use crate::{SUFFIX_LENGTH, Suffix};
pub fn detect(file: &mut std::fs::File) -> Result<bool, Error> {
file.rewind()?;
let mut signature = [0; 5];
file.read_exact(&mut signature)?;
let Ok(suffix) = Suffix::from_file(file) else {
return Err(Error::InvalidSuffix);
};
Ok(&signature == b"DfuSe" && suffix.bcdDFU == 0x011A)
}
#[derive(Debug)]
pub struct Content {
pub prefix: Prefix,
pub images: Vec<Image>,
}
impl Content {
pub fn new(prefix: Prefix, images: Vec<Image>) -> Self {
Self { prefix, images }
}
pub fn from_file(file: &mut std::fs::File) -> Result<Self, Error> {
let file_size = file.seek(std::io::SeekFrom::End(0))?;
if file_size < (PREFIX_LENGTH + SUFFIX_LENGTH) as u64 {
return Err(Error::InsufficientFileSize);
}
let prefix = Prefix::from_file(file)?;
let mut images = Vec::new();
let mut file_pos = PREFIX_LENGTH as u64;
for _ in 0..prefix.bTargets {
let image = Image::from_file(file, &mut file_pos)?;
images.push(image);
}
let content = Self::new(prefix, images);
Ok(content)
}
pub fn find_image_by_alt(&self, alt_setting: u8) -> Option<&Image> {
self.images
.iter()
.find(|&image| image.target_prefix.bAlternateSetting == alt_setting)
}
pub fn find_image_by_name<T: AsRef<str>>(&self, name: T) -> Option<&Image> {
self.images
.iter()
.find(|&image| image.target_prefix.szTargetName == name.as_ref())
}
}
pub const PREFIX_LENGTH: usize = 11;
#[allow(non_snake_case)]
#[derive(Debug, Clone)]
pub struct Prefix {
pub szSignature: String,
pub bVersion: u8,
pub DFUImageSize: u32,
pub bTargets: u8,
}
impl Default for Prefix {
fn default() -> Self {
Self {
szSignature: String::from("DfuSe"),
bVersion: 1,
DFUImageSize: 0,
bTargets: 0,
}
}
}
impl Prefix {
pub fn new(signature: String, version: u8, image_size: u32, num_targets: u8) -> Self {
Self {
szSignature: signature,
bVersion: version,
DFUImageSize: image_size,
bTargets: num_targets,
}
}
pub fn from_bytes(buffer: &[u8; PREFIX_LENGTH]) -> Self {
Self::new(
String::from_utf8_lossy(&buffer[0..5]).to_string(),
u8::from_le(buffer[5]),
u32::from_le_bytes([buffer[6], buffer[7], buffer[8], buffer[9]]),
u8::from_le(buffer[10]),
)
}
pub fn from_file(file: &mut std::fs::File) -> Result<Self, Error> {
file.rewind()?;
let mut buffer = [0; PREFIX_LENGTH];
file.read_exact(&mut buffer)?;
let data = Self::from_bytes(&buffer);
if &data.szSignature != "DfuSe" {
return Err(Error::InvalidPrefixSignature);
}
Ok(data)
}
}
#[derive(Debug, Clone)]
pub struct Image {
pub target_prefix: TargetPrefix,
pub image_elements: Vec<ImageElement>,
}
impl Default for Image {
fn default() -> Self {
Self {
target_prefix: TargetPrefix::default(),
image_elements: Vec::new(),
}
}
}
impl Image {
pub fn new(target_prefix: TargetPrefix, image_elements: Vec<ImageElement>) -> Self {
Self {
target_prefix,
image_elements,
}
}
pub fn from_file(file: &mut std::fs::File, file_pos: &mut u64) -> Result<Self, Error> {
let target_prefix = TargetPrefix::from_file(file, file_pos)?;
let mut image_elements = Vec::new();
for _ in 0..target_prefix.dwNbElements {
let image_element = ImageElement::from_file(file, file_pos)?;
image_elements.push(image_element);
}
let image = Image::new(target_prefix, image_elements);
Ok(image)
}
}
pub const TARGET_PREFIX_LENGTH: usize = 274;
#[allow(non_snake_case)]
#[derive(Debug, Clone)]
pub struct TargetPrefix {
pub szSignature: String,
pub bAlternateSetting: u8,
pub bTargetNamed: u8,
pub szTargetName: String,
pub dwTargetSize: u32,
pub dwNbElements: u32,
}
impl Default for TargetPrefix {
fn default() -> Self {
Self {
szSignature: String::from("Target"),
bAlternateSetting: 0,
bTargetNamed: 0,
szTargetName: String::new(),
dwTargetSize: 0,
dwNbElements: 0,
}
}
}
impl TargetPrefix {
pub fn new(
signature: String,
alt_setting: u8,
named: u8,
target_name: String,
target_size: u32,
num_elements: u32,
) -> Self {
Self {
szSignature: signature,
bAlternateSetting: alt_setting,
bTargetNamed: named,
szTargetName: target_name,
dwTargetSize: target_size,
dwNbElements: num_elements,
}
}
pub fn from_bytes(buffer: &[u8; TARGET_PREFIX_LENGTH]) -> Self {
let target_name_full = String::from_utf8_lossy(&buffer[11..266]).to_string();
let target_name_len = target_name_full.find('\x00');
let target_name_len = target_name_len.unwrap_or(255);
Self::new(
String::from_utf8_lossy(&buffer[0..6]).to_string(),
u8::from_le(buffer[6]),
u8::from_le(buffer[7]),
String::from_utf8_lossy(&buffer[11..266])[0..target_name_len].to_string(),
u32::from_le_bytes([buffer[266], buffer[267], buffer[268], buffer[269]]),
u32::from_le_bytes([buffer[270], buffer[271], buffer[272], buffer[273]]),
)
}
pub fn from_file(file: &mut std::fs::File, file_pos: &mut u64) -> Result<Self, Error> {
file.seek(std::io::SeekFrom::Start(*file_pos))?;
let mut buffer = [0; TARGET_PREFIX_LENGTH];
file.read_exact(&mut buffer)?;
*file_pos += TARGET_PREFIX_LENGTH as u64;
let data = Self::from_bytes(&buffer);
if &data.szSignature != "Target" {
return Err(Error::InvalidTargetPrefixSignature);
}
Ok(data)
}
}
pub const IMAGE_ELEMENT_LENGTH: usize = 8;
#[allow(non_snake_case)]
#[derive(Debug, Clone)]
pub struct ImageElement {
pub dwElementAddress: u32,
pub dwElementSize: u32,
pub data_position: u64,
}
impl Default for ImageElement {
fn default() -> Self {
Self {
dwElementAddress: 0,
dwElementSize: 0,
data_position: 0,
}
}
}
impl ImageElement {
pub fn new(element_address: u32, element_size: u32, data_position: u64) -> Self {
Self {
dwElementAddress: element_address,
dwElementSize: element_size,
data_position,
}
}
pub fn from_bytes(buffer: &[u8; IMAGE_ELEMENT_LENGTH], data_position: u64) -> Self {
Self::new(
u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]),
u32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
data_position,
)
}
pub fn from_file(file: &mut std::fs::File, file_pos: &mut u64) -> Result<Self, Error> {
file.seek(std::io::SeekFrom::Start(*file_pos))?;
let mut buffer = [0; IMAGE_ELEMENT_LENGTH];
file.read_exact(&mut buffer)?;
*file_pos += IMAGE_ELEMENT_LENGTH as u64;
let data = Self::from_bytes(&buffer, *file_pos);
*file_pos += data.dwElementSize as u64;
Ok(data)
}
pub fn read_at(
&self,
file: &mut std::fs::File,
position: u32,
buffer: &mut [u8],
) -> Result<usize, Error> {
let file_pos = self.data_position + (position as u64);
file.seek(std::io::SeekFrom::Start(file_pos))?;
let read_size = file.read(buffer)?;
let read_size = std::cmp::min(read_size, (self.dwElementSize - position) as usize);
Ok(read_size)
}
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Invalid file prefix signature.")]
InvalidPrefixSignature,
#[error("Invalid file suffix.")]
InvalidSuffix,
#[error("Invalid target prefix signature.")]
InvalidTargetPrefixSignature,
#[error("File size is to small to contain prefix and suffix")]
InsufficientFileSize,
#[error(transparent)]
Io(#[from] std::io::Error),
}