1use core::cmp::{max, min};
2
3use deku::DekuRead;
4use serde::{Deserialize, Serialize};
5use strum::{EnumIter, EnumString, FromRepr, IntoEnumIterator, VariantNames};
6
7pub(crate) use self::de::{DeserializedBinPartition, DeserializedCsvPartition};
8
9mod de;
10
11pub(crate) const APP_PARTITION_ALIGNMENT: u32 = 0x10000;
12pub(crate) const DATA_PARTITION_ALIGNMENT: u32 = 0x1000;
13pub(crate) const MAX_NAME_LEN: usize = 16;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, DekuRead)]
25#[deku(endian = "little", id_type = "u8")]
26#[serde(rename_all = "lowercase")]
27pub enum Type {
28 #[deku(id = "0x00")]
29 App,
30 #[deku(id = "0x01")]
31 Data,
32 #[deku(id_pat = "0x02..=0xFE")]
33 Custom(u8),
34}
35
36impl core::fmt::Display for Type {
37 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
38 write!(
39 f,
40 "{}",
41 match self {
42 Type::App | Type::Data => serde_plain::to_string(self).unwrap(),
43 Type::Custom(ty) => serde_plain::to_string(&format_args!("{:#04x}", ty)).unwrap(),
44 }
45 )
46 }
47}
48
49impl From<u8> for Type {
50 fn from(ty: u8) -> Self {
51 match ty {
52 0x00 => Type::App,
53 0x01 => Type::Data,
54 ty => Type::Custom(ty),
55 }
56 }
57}
58
59impl From<Type> for u8 {
60 fn from(value: Type) -> Self {
61 match value {
62 Type::App => 0x00,
63 Type::Data => 0x01,
64 Type::Custom(ty) => ty,
65 }
66 }
67}
68
69impl Type {
70 pub fn subtype_hint(&self) -> String {
74 match self {
75 Type::App => "'factory', 'ota_0' through 'ota_15', or 'test'".into(),
76 Type::Data => {
77 let types = DataType::iter()
78 .map(|dt| format!("'{}'", serde_plain::to_string(&dt).unwrap()))
79 .collect::<Vec<_>>();
80
81 let (tail, head) = types.split_last().unwrap();
82
83 format!("{}, and {}", head.join(", "), tail)
84 }
85 Type::Custom(..) => "0x02 through 0xFE".into(),
86 }
87 }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
99#[serde(untagged)]
100pub enum SubType {
101 App(AppType),
102 Data(DataType),
103 Custom(u8),
104}
105
106impl core::fmt::Display for SubType {
107 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
108 write!(
109 f,
110 "{}",
111 match self {
112 SubType::App(ty) => serde_plain::to_string(ty).unwrap(),
113 SubType::Data(ty) => serde_plain::to_string(ty).unwrap(),
114 SubType::Custom(ty) =>
115 serde_plain::to_string(&format_args!("{:#04x}", ty)).unwrap(),
116 }
117 )
118 }
119}
120
121impl From<AppType> for SubType {
122 fn from(ty: AppType) -> Self {
123 SubType::App(ty)
124 }
125}
126
127impl From<DataType> for SubType {
128 fn from(ty: DataType) -> Self {
129 SubType::Data(ty)
130 }
131}
132
133impl From<u8> for SubType {
134 fn from(ty: u8) -> Self {
135 SubType::Custom(ty)
136 }
137}
138
139impl From<SubType> for u8 {
140 fn from(value: SubType) -> Self {
141 match value {
142 SubType::App(ty) => ty as u8,
143 SubType::Data(ty) => ty as u8,
144 SubType::Custom(ty) => ty,
145 }
146 }
147}
148
149impl SubType {
150 pub fn app(value: u8) -> Self {
152 Self::App(AppType::from_repr(value as usize).unwrap())
153 }
154
155 pub fn data(value: u8) -> Self {
157 Self::Data(DataType::from_repr(value as usize).unwrap())
158 }
159}
160
161#[allow(non_camel_case_types)]
166#[derive(
167 Debug,
168 Clone,
169 Copy,
170 PartialEq,
171 Eq,
172 Deserialize,
173 EnumIter,
174 EnumString,
175 VariantNames,
176 FromRepr,
177 Serialize,
178 DekuRead,
179)]
180#[deku(endian = "little", id_type = "u8")]
181#[serde(rename_all = "snake_case")]
182#[strum(serialize_all = "snake_case")]
183pub enum AppType {
184 Factory = 0x00,
185 Ota_0 = 0x10,
186 Ota_1 = 0x11,
187 Ota_2 = 0x12,
188 Ota_3 = 0x13,
189 Ota_4 = 0x14,
190 Ota_5 = 0x15,
191 Ota_6 = 0x16,
192 Ota_7 = 0x17,
193 Ota_8 = 0x18,
194 Ota_9 = 0x19,
195 Ota_10 = 0x1A,
196 Ota_11 = 0x1B,
197 Ota_12 = 0x1C,
198 Ota_13 = 0x1D,
199 Ota_14 = 0x1E,
200 Ota_15 = 0x1F,
201 Test = 0x20,
202}
203
204#[derive(
209 Debug,
210 Clone,
211 Copy,
212 PartialEq,
213 Eq,
214 Deserialize,
215 EnumIter,
216 EnumString,
217 VariantNames,
218 FromRepr,
219 Serialize,
220 DekuRead,
221)]
222#[deku(endian = "little", id_type = "u8")]
223#[serde(rename_all = "snake_case")]
224#[strum(serialize_all = "snake_case")]
225pub enum DataType {
226 Ota = 0x00,
227 Phy = 0x01,
228 Nvs = 0x02,
229 Coredump = 0x03,
230 NvsKeys = 0x04,
231 #[serde(rename = "efuse")]
232 #[strum(serialize = "efuse")]
233 EfuseEm = 0x05,
234 Undefined = 0x06,
235 Esphttpd = 0x80,
236 Fat = 0x81,
237 Spiffs = 0x82,
238 Littlefs = 0x83,
239}
240
241bitflags::bitflags! {
242 #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
264 pub struct Flags: u32 {
265 const ENCRYPTED = 0b0001;
267 const READONLY = 0b0010;
269 }
270}
271
272#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
274pub struct Partition {
275 name: String,
276 ty: Type,
277 subtype: SubType,
278 offset: u32,
279 size: u32,
280 flags: Flags,
281}
282
283impl Partition {
284 pub fn new<S>(name: S, ty: Type, subtype: SubType, offset: u32, size: u32, flags: Flags) -> Self
286 where
287 S: Into<String>,
288 {
289 Self {
290 name: name.into(),
291 ty,
292 subtype,
293 offset,
294 size,
295 flags,
296 }
297 }
298
299 pub fn name(&self) -> String {
301 self.name.clone()
302 }
303
304 pub fn ty(&self) -> Type {
306 self.ty
307 }
308
309 pub fn subtype(&self) -> SubType {
311 self.subtype
312 }
313
314 pub fn offset(&self) -> u32 {
316 self.offset
317 }
318
319 pub fn size(&self) -> u32 {
321 self.size
322 }
323
324 pub fn flags(&self) -> Flags {
326 self.flags
327 }
328
329 pub fn overlaps(&self, other: &Partition) -> bool {
331 max(self.offset, other.offset) < min(self.offset + self.size, other.offset + other.size)
332 }
333
334 pub fn write_bin<W>(&self, writer: &mut W) -> std::io::Result<()>
336 where
337 W: std::io::Write,
338 {
339 const MAGIC_BYTES: [u8; 2] = [0xAA, 0x50];
340
341 writer.write_all(&MAGIC_BYTES)?;
342 writer.write_all(&[self.ty.into(), self.subtype.into()])?;
343 writer.write_all(&self.offset.to_le_bytes())?;
344 writer.write_all(&self.size.to_le_bytes())?;
345
346 let mut name_bytes = [0u8; 16];
347 for (source, dest) in self.name.bytes().zip(name_bytes.iter_mut()) {
348 *dest = source;
349 }
350 writer.write_all(&name_bytes)?;
351
352 writer.write_all(&self.flags.bits().to_le_bytes())?;
353
354 Ok(())
355 }
356
357 pub fn write_csv<W>(&self, csv: &mut csv::Writer<W>) -> std::io::Result<()>
359 where
360 W: std::io::Write,
361 {
362 let mut flags = Vec::<&str>::new();
363 if self.flags.contains(Flags::ENCRYPTED) {
364 flags.push("encrypted");
365 }
366 if self.flags.contains(Flags::READONLY) {
367 flags.push("readonly");
368 }
369
370 let flags = flags.join(":");
371
372 csv.write_record(&[
373 self.name(),
374 self.ty.to_string(),
375 self.subtype.to_string(),
376 format!("{:#x}", self.offset),
377 format!("{:#x}", self.size),
378 flags,
379 ])?;
380
381 Ok(())
382 }
383}