1use std::{
2 fs::File,
3 io::{BufRead, BufReader, Write},
4 path::Path,
5};
6
7use anyhow::{anyhow, Result};
8use families::{get_family_id, ChipFamily};
9
10use crate::block::Block;
11
12pub mod block;
13pub mod families;
14
15const ADDRESS_MASK: u32 = 0xff;
16const INVERTED_ADDRESS_MASK: u32 = !ADDRESS_MASK;
17
18pub fn hex_to_uf2(
19 hex_lines: impl Iterator<Item = impl AsRef<str>>,
20 family: Option<ChipFamily>,
21) -> Result<Vec<u8>> {
22 let mut upper: u32 = 0;
23 let mut app_start_address: Option<u32> = None;
24
25 let mut current_block: Option<Block> = None;
26
27 let mut blocks: Vec<Block> = vec![];
28
29 for hex_line in hex_lines {
30 let hex_line = hex_line.as_ref();
31 if !hex_line.starts_with(':') {
32 continue;
33 }
34
35 let mut binary_line = vec![];
36 let mut i = 1;
37
38 while i < hex_line.len() - 1 {
39 let byte = u8::from_str_radix(&hex_line[i..i + 2], 16).unwrap_or(0);
40 binary_line.push(byte);
41 i += 2;
42 }
43
44 let byte_count = binary_line[0] as usize;
45 if binary_line.len() - 5 != byte_count {
46 return Err(anyhow!("Invalid HEX line: mismatched byte count"));
47 }
48
49 if binary_line.len() < 4 {
50 return Err(anyhow!(
51 "Line with less than 4 encoded hex chars + 1 byte leads to undefined behaviour!"
52 ));
53 }
54
55 match binary_line[3] {
56 0 => {
57 let mut address =
58 upper + (((binary_line[1] as u32) << 8) | (binary_line[2] as u32));
59 if app_start_address.is_none() {
60 app_start_address = Some(address);
61 }
62 for byte in binary_line.iter().take(binary_line.len() - 1).skip(4) {
64 if current_block.is_none()
65 || (current_block.as_ref().unwrap().address & INVERTED_ADDRESS_MASK)
66 != (address & INVERTED_ADDRESS_MASK)
67 {
68 if let Some(block) = current_block.take() {
69 blocks.push(block);
70 }
71 current_block = Some(Block::new(address & INVERTED_ADDRESS_MASK));
72 }
73 let current_mut_block = current_block
74 .as_mut()
75 .expect("current_block should always be some here");
76
77 current_mut_block.bytes[(address & ADDRESS_MASK) as usize] = *byte;
78 address += 1;
79 }
80 }
81 1 => break, 2 => {
83 upper = (((binary_line[4] as u32) << 8) | (binary_line[5] as u32)) << 4;
84 }
85 4 => {
86 upper = (((binary_line[4] as u32) << 8) | (binary_line[5] as u32)) << 16;
87 }
88 _ => {
89 }
91 }
92 }
93
94 if let Some(block) = current_block.take() {
95 blocks.push(block);
97 }
98
99 let number_of_blocks = blocks.len() as u32;
100
101 let family_id = family.map(get_family_id);
102
103 Ok(blocks
104 .iter()
105 .enumerate()
106 .flat_map(|(i, block)| {
107 block
108 .encode(i as u32, number_of_blocks, family_id)
109 .into_iter()
110 })
111 .collect())
112}
113
114pub fn hex_to_uf2_file(
115 hex_file: &Path,
116 output_path: &Path,
117 family: Option<ChipFamily>,
118) -> Result<()> {
119 let binary_buffer = BufReader::new(File::open(hex_file).expect("Couldn't open input file!"));
120
121 let uf2_buffer = hex_to_uf2(
122 binary_buffer
123 .lines()
124 .map(|line| line.expect("error reading line")),
125 family,
126 )
127 .expect("Error converting hex to uf2");
128
129 let mut file =
130 File::create(output_path).expect("Error creating or overwriting destination file");
131
132 file.write_all(&uf2_buffer)
133 .expect("Error writing to destination");
134
135 Ok(())
136}
137
138#[cfg(test)]
139mod tests {
140 use std::io::Read;
141
142 use super::*;
143
144 #[test]
145 fn static_string() {
146 let static_string = ":020000041000EA
147:1000000000B5324B212058609868022188439860DF
148:10001000D860186158612E4B002199600221596106
149:100020000121F02299502B49196001219960352056
150:1000300000F044F80222904214D00621196600F024
151:1000400034F8196E01211966002018661A6600F04E
152:100050002CF8196E196E196E052000F02FF8012189
153:100060000842F9D1002199601B49196000215960AB";
154
155 let uf2_bytes = hex_to_uf2(static_string.lines(), None);
156
157 println!("{uf2_bytes:X?}");
158 }
159
160 fn compare_to_python(
161 input: &Path,
162 output: &Path,
163 python_out: &Path,
164 family: Option<ChipFamily>,
165 ) {
166 hex_to_uf2_file(input, output, family).unwrap();
167
168 let rust_reader = BufReader::new(File::open(output).unwrap());
169 let python_reader = BufReader::new(File::open(python_out).unwrap());
170
171 let mut rust_bytes = rust_reader.bytes();
172 let mut python_bytes = python_reader.bytes();
173
174 let mut position: usize = 0;
175
176 loop {
177 match (rust_bytes.next(), python_bytes.next()) {
178 (None, None) => break,
179 (Some(_), None) => {
180 assert!(false, "rust_bytes is longer than python_bytes");
181 break;
182 }
183 (None, Some(_)) => {
184 assert!(false, "python_bytes is longer than rust_bytes");
185 break;
186 }
187 (Some(rust_byte), Some(python_byte)) => {
188 assert_eq!(
189 rust_byte.unwrap(),
190 python_byte.unwrap(),
191 "Difference at {position}, hex: {position:X}"
192 );
193 }
194 }
195
196 position += 1;
197 }
198 }
199
200 #[test]
201 fn no_family_should_be_same() {
202 compare_to_python(
203 Path::new("./test/fenris-rmk-central.hex"),
204 Path::new("./test/fenris-rmk-central.uf2"),
205 Path::new("./test/fenris-rmk-central_py.uf2"),
206 None,
207 );
208 }
209
210 #[test]
211 fn family_should_be_same() {
212 compare_to_python(
213 Path::new("./test/fenris-rmk-central.hex"),
214 Path::new("./test/fenris-rmk-central_id.uf2"),
215 Path::new("./test/fenris-rmk-central_py_id.uf2"),
216 Some(ChipFamily::RP2040),
217 );
218 }
219}