uf2_decode/
lib.rs

1//! # uf2-decode
2//!
3//! [![uf2-decode on crates.io](https://img.shields.io/crates/d/uf2-decode)](https://crates.io/crates/uf2-decode)
4//! [![uf2-decode on docs.rs](https://img.shields.io/docsrs/uf2-decode)](https://docs.rs/uf2-decode/latest/uf2_decode/)
5//!
6//! Basic decoding of UF2.  This is a partial
7//! adaptation of
8//! [`uf2conv.py`](https://github.com/microsoft/uf2/blob/17e70bf908e6abdf4f4acc50a9c84e5709ded2a9/utils/uf2conv.py).
9
10use std::collections::HashMap;
11
12#[derive(Debug)]
13pub enum Error {
14    TooMuchPaddingRequired(usize),
15    NonWordPaddingSize(usize),
16    InvalidDataSize(usize),
17}
18
19/// Takes a UF2 and returns raw bin coupled with family ID-target address pairs
20///
21/// # Panics
22///
23/// - If slices can't be chunked into arrays of the right sizes
24/// - Current address wasn't set when it should have been
25///
26/// # Errors
27///
28/// - [`Error::TooMuchPaddingRequired`]
29/// - [`Error::NonWordPaddingSize`]
30/// - [`Error::InvalidDataSize`]
31pub fn convert_from_uf2(buf: &[u8]) -> Result<(Vec<u8>, HashMap<u32, u64>), Error> {
32    let mut curr_addr: Option<usize> = None;
33    let mut curr_family_id: Option<u32> = None;
34    let mut families_found: HashMap<u32, u64> = HashMap::new();
35    let mut outp: Vec<u8> = Vec::new();
36
37    for (index, block) in buf.chunks_exact(512).enumerate() {
38        let hd: [u32; 8] = block[0..32]
39            .chunks_exact(4)
40            .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap()))
41            .collect::<Vec<u32>>()
42            .try_into()
43            .unwrap();
44        // Skipping block at with bad magic or NO-flash flag set; skip block
45        if (hd[0], hd[1]) != (0x0A32_4655, 0x9E5D_5157) || (hd[2] & 1) != 0 {
46            continue;
47        }
48        let data_len = hd[4] as usize;
49        if data_len > 476 {
50            return Err(Error::InvalidDataSize(index));
51        }
52        let new_addr = hd[3] as usize;
53        if (hd[2] & 0x2000) != 0 && curr_family_id.is_none() {
54            curr_family_id = Some(hd[7]);
55        }
56
57        if curr_addr.is_none() || ((hd[2] & 0x2000) != 0 && Some(hd[7]) != curr_family_id) {
58            curr_family_id = Some(hd[7]);
59            curr_addr = Some(new_addr);
60        }
61        let mut padding = new_addr - curr_addr.unwrap();
62        if padding > 10 * 1024 * 1024 {
63            return Err(Error::TooMuchPaddingRequired(index));
64        }
65        if padding % 4 != 0 {
66            return Err(Error::NonWordPaddingSize(index));
67        }
68        while padding > 0 {
69            padding -= 4;
70            outp.extend_from_slice(&[0x0, 0x0, 0x0, 0x0]);
71        }
72
73        if (hd[2] & 0x2000) != 0 {
74            outp.extend_from_slice(&block[32..(32 + data_len)]);
75        }
76        curr_addr = Some(new_addr + data_len);
77        if (hd[2] & 0x2000) != 0 {
78            match families_found.get(&hd[7]) {
79                Some(v) if *v > new_addr.try_into().unwrap() => {
80                    families_found.insert(hd[7], new_addr.try_into().unwrap());
81                }
82                None => {
83                    families_found.insert(hd[7], new_addr.try_into().unwrap());
84                }
85                _ => (),
86            }
87        }
88    }
89
90    Ok((outp, families_found))
91}