1#[cfg(feature = "serde-derives")]
3use serde::Serialize;
4use std::{collections::BTreeMap, fmt::Debug, str::FromStr};
5
6#[derive(PartialEq)]
8pub enum Value {
9 String(String),
11 I32(i32),
13 F32(f32),
15 U32(u32),
17 Bool(bool),
19 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#[cfg_attr(feature = "serde-derives", derive(Serialize))]
58#[cfg_attr(feature = "serde-derives", serde(transparent))]
59pub struct LDF {
60 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#[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}