Skip to main content

mint_cli/layout/
settings.rs

1use serde::Deserialize;
2
3#[derive(Debug, Deserialize)]
4pub struct Settings {
5    pub endianness: Endianness,
6    #[serde(default = "default_offset")]
7    pub virtual_offset: u32,
8    #[serde(default)]
9    pub word_addressing: bool,
10    #[serde(default)]
11    pub crc: Option<CrcConfig>,
12}
13
14#[derive(Debug, Deserialize, Clone, Copy)]
15#[serde(rename_all = "lowercase")]
16pub enum Endianness {
17    Little,
18    Big,
19}
20
21#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq, Default)]
22pub enum CrcArea {
23    #[default]
24    #[serde(rename = "data")]
25    Data,
26    #[serde(rename = "block_zero_crc")]
27    BlockZeroCrc,
28    #[serde(rename = "block_pad_crc")]
29    BlockPadCrc,
30    #[serde(rename = "block_omit_crc")]
31    BlockOmitCrc,
32}
33
34/// CRC location: keyword or absolute address.
35/// - `"end_data"`: CRC placed after data (4-byte aligned)
36/// - `"end_block"`: CRC in final 4 bytes of block
37/// - `0x8FF0`: Absolute address within block
38#[derive(Debug, Deserialize, Clone)]
39#[serde(untagged)]
40pub enum CrcLocation {
41    Keyword(String),
42    Address(u32),
43}
44
45/// Unified CRC configuration used in both `[settings.crc]` and `[header.crc]`.
46/// All fields are optional; header values override settings values.
47/// At settings level, `location` must be "end_data" or "end_block" (not an address).
48#[derive(Debug, Deserialize, Clone, Default)]
49pub struct CrcConfig {
50    pub location: Option<CrcLocation>,
51    pub polynomial: Option<u32>,
52    pub start: Option<u32>,
53    pub xor_out: Option<u32>,
54    pub ref_in: Option<bool>,
55    pub ref_out: Option<bool>,
56    pub area: Option<CrcArea>,
57}
58
59impl CrcConfig {
60    /// Merge this config with a base config. Self takes precedence.
61    pub fn resolve(&self, base: Option<&CrcConfig>) -> CrcConfig {
62        CrcConfig {
63            location: self
64                .location
65                .clone()
66                .or_else(|| base.and_then(|b| b.location.clone())),
67            polynomial: self.polynomial.or_else(|| base.and_then(|b| b.polynomial)),
68            start: self.start.or_else(|| base.and_then(|b| b.start)),
69            xor_out: self.xor_out.or_else(|| base.and_then(|b| b.xor_out)),
70            ref_in: self.ref_in.or_else(|| base.and_then(|b| b.ref_in)),
71            ref_out: self.ref_out.or_else(|| base.and_then(|b| b.ref_out)),
72            area: self.area.or_else(|| base.and_then(|b| b.area)),
73        }
74    }
75
76    /// Check if CRC is disabled (location not set).
77    pub fn is_disabled(&self) -> bool {
78        self.location.is_none()
79    }
80
81    /// Returns true if all required CRC parameters are present.
82    pub fn is_complete(&self) -> bool {
83        self.polynomial.is_some()
84            && self.start.is_some()
85            && self.xor_out.is_some()
86            && self.ref_in.is_some()
87            && self.ref_out.is_some()
88            && self.area.is_some()
89    }
90}
91
92fn default_offset() -> u32 {
93    0
94}
95
96pub trait EndianBytes {
97    fn to_endian_bytes(self, endianness: &Endianness) -> Vec<u8>;
98}
99
100macro_rules! impl_endian_bytes {
101    ($($t:ty),* $(,)?) => {$(
102        impl EndianBytes for $t {
103            fn to_endian_bytes(self, e: &Endianness) -> Vec<u8> {
104                match e {
105                    Endianness::Little => self.to_le_bytes().to_vec(),
106                    Endianness::Big => self.to_be_bytes().to_vec(),
107                }
108            }
109        }
110    )*};
111}
112impl_endian_bytes!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);