assembly_core/
ldf.rs

1//! The LEGO data format
2#[cfg(feature = "serde-derives")]
3use serde::Serialize;
4use std::{collections::BTreeMap, fmt::Debug, str::FromStr};
5
6/// A LEGO-Data-Format value
7#[derive(PartialEq)]
8pub enum Value {
9    /// A user-facing string
10    String(String),
11    /// A signed 32bit integer
12    I32(i32),
13    /// A single precision floating point number
14    F32(f32),
15    /// An unsigned 32bit interger
16    U32(u32),
17    /// A boolean (0 or 1)
18    Bool(bool),
19    /// An internal string
20    Bytes(String),
21}
22
23#[cfg(feature = "serde-derives")]
24impl Serialize for Value {
25    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26    where
27        S: serde::Serializer,
28    {
29        match self {
30            Self::String(s) => serializer.serialize_str(s.as_str()),
31            Self::I32(i) => serializer.serialize_i32(*i),
32            Self::F32(i) => serializer.serialize_f32(*i),
33            Self::U32(i) => serializer.serialize_u32(*i),
34            Self::Bool(b) => serializer.serialize_bool(*b),
35            Self::Bytes(b) => serializer.serialize_str(b.as_str()),
36        }
37    }
38}
39
40impl Debug for Value {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        match self {
43            Self::String(s) => s.fmt(f),
44            Self::I32(i) => i.fmt(f),
45            Self::F32(l) => l.fmt(f),
46            Self::U32(u) => u.fmt(f),
47            Self::Bool(b) => b.fmt(f),
48            Self::Bytes(s) => {
49                write!(f, "b")?;
50                s.fmt(f)
51            }
52        }
53    }
54}
55
56/// A table of LDF values
57#[cfg_attr(feature = "serde-derives", derive(Serialize))]
58#[cfg_attr(feature = "serde-derives", serde(transparent))]
59pub struct LDF {
60    /// The contained map
61    pub map: BTreeMap<String, Value>,
62}
63
64impl Debug for LDF {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        let mut d = f.debug_struct("LDF");
67        for (name, value) in self.map.iter() {
68            d.field(name, value);
69        }
70        d.finish()
71    }
72}
73
74/// Error when parsing LDF
75#[derive(Debug)]
76pub struct LDFError(u8, String);
77
78impl FromStr for LDF {
79    type Err = LDFError;
80    fn from_str(s: &str) -> Result<Self, Self::Err> {
81        s.split('\n')
82            .try_fold(BTreeMap::new(), |mut x, y| {
83                let mut out = y.splitn(2, '=');
84                let key = out.next().unwrap();
85                let val = out.next().ok_or_else(|| LDFError(0, key.to_string()))?;
86                let mut inn = val.splitn(2, ':');
87                let typ = inn.next().unwrap();
88                let z = inn.next().ok_or_else(|| LDFError(1, key.to_string()))?;
89
90                let v = match typ {
91                    "0" => Value::String(z.into()),
92                    "1" => Value::I32(z.parse().map_err(|_| LDFError(2, key.to_string()))?),
93                    "3" => Value::F32(z.parse().map_err(|_| LDFError(3, z.to_string()))?),
94                    "5" => Value::U32(z.parse().map_err(|_| LDFError(4, key.to_string()))?),
95                    "7" => Value::Bool(match z {
96                        "0" => false,
97                        "1" => true,
98                        _ => return Err(LDFError(5, key.to_string())),
99                    }),
100                    "13" => Value::Bytes(z.into()),
101                    _ => return Err(LDFError(6, key.to_string())),
102                };
103                x.insert(key.to_string(), v);
104                Ok(x)
105            })
106            .map(|map| LDF { map })
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::{Value, LDF};
113
114    #[test]
115    fn test_from_str() {
116        let text = "CamRlBiasAmt=3:0\nCamRlBiasBi=7:0\nCamRlBiasFwd=7:0\nCamRlBiasRet=7:0\nCamRlCamPath=0:\nCamRlCamRetPath=0:\nCamRlFaceTgt=7:1\nCamRlPosPath=0:\nCheckPrecondition=0:\nNDAEG=13:\nadd_to_navmesh=7:1\ncamBehaviorDirectional=7:0\ncamBehaviorPitch=7:0\ncamBehaviorYaw=7:0\ncamDir=0:\ncamGradSnap=7:0\ncamLkDir=7:0\ncamPitchAngleDown=3:120\ncamPitchAngleUp=3:60\ncamPrefersToFadeObject=7:1\ncancelBehaviorMovement=7:0\ncarver_only=7:0\ncreate_physics=7:1\ncustom_config_names=0:\nfxpriority=1:0\nignoreCameraCollision=7:0\ninteraction_distance=3:16\nis_smashable=7:0\nloadOnClientOnly=7:0\nloadSrvrOnly=7:0\nnavmesh_carver=7:0\nrenderCullingGroup=5:0\nrespawnVol=7:0\nrespawnVolName=0:\nrlLeadIn=3:1\nrlLeadOut=3:-1\nrspPos=0:0.0000\u{1f}0.0000\u{1f}0.0000\nrspRot=0:1.0000 \u{1f} 0.0000 \u{1f} 0.0000 \u{1f} 0.0000\nsceneIDOverride=1:0\nsceneIDOverrideEnabled=7:1\nsceneLayerIDOverride=5:0\nsetsRailCam=7:0\ntemplate=1:-1";
117        let map: LDF = text.parse().unwrap();
118
119        let mut res = vec![
120            ("CamRlBiasAmt", Value::F32(0.0)),
121            ("CamRlBiasBi", Value::Bool(false)),
122            ("CamRlBiasFwd", Value::Bool(false)),
123            ("CamRlBiasRet", Value::Bool(false)),
124            ("CamRlCamPath", Value::String(String::new())),
125            ("CamRlCamRetPath", Value::String(String::new())),
126            ("CamRlFaceTgt", Value::Bool(true)),
127            ("CamRlPosPath", Value::String(String::new())),
128            ("CheckPrecondition", Value::String(String::new())),
129            ("NDAEG", Value::Bytes(String::new())),
130            ("add_to_navmesh", Value::Bool(true)),
131            ("camBehaviorDirectional", Value::Bool(false)),
132            ("camBehaviorPitch", Value::Bool(false)),
133            ("camBehaviorYaw", Value::Bool(false)),
134            ("camDir", Value::String(String::new())),
135            ("camGradSnap", Value::Bool(false)),
136            ("camLkDir", Value::Bool(false)),
137            ("camPitchAngleDown", Value::F32(120.0)),
138            ("camPitchAngleUp", Value::F32(60.0)),
139            ("camPrefersToFadeObject", Value::Bool(true)),
140            ("cancelBehaviorMovement", Value::Bool(false)),
141            ("carver_only", Value::Bool(false)),
142            ("create_physics", Value::Bool(true)),
143            ("custom_config_names", Value::String(String::new())),
144            ("fxpriority", Value::I32(0)),
145            ("ignoreCameraCollision", Value::Bool(false)),
146            ("interaction_distance", Value::F32(16.0)),
147            ("is_smashable", Value::Bool(false)),
148            ("loadOnClientOnly", Value::Bool(false)),
149            ("loadSrvrOnly", Value::Bool(false)),
150            ("navmesh_carver", Value::Bool(false)),
151            ("renderCullingGroup", Value::U32(0)),
152            ("respawnVol", Value::Bool(false)),
153            ("respawnVolName", Value::String(String::new())),
154            ("rlLeadIn", Value::F32(1.0)),
155            ("rlLeadOut", Value::F32(-1.0)),
156            (
157                "rspPos",
158                Value::String("0.0000\u{1f}0.0000\u{1f}0.0000".to_string()),
159            ),
160            (
161                "rspRot",
162                Value::String("1.0000 \u{1f} 0.0000 \u{1f} 0.0000 \u{1f} 0.0000".to_string()),
163            ),
164            ("sceneIDOverride", Value::I32(0)),
165            ("sceneIDOverrideEnabled", Value::Bool(true)),
166            ("sceneLayerIDOverride", Value::U32(0)),
167            ("setsRailCam", Value::Bool(false)),
168            ("template", Value::I32(-1)),
169        ];
170
171        let mut r = res.drain(..);
172        for (key, value) in &map.map {
173            let (rk, rv) = r.next().unwrap();
174            assert_eq!((rk, &rv), (key.as_str(), value));
175        }
176
177        assert_eq!(r.len(), 0);
178    }
179}