1use derive_more::{Deref, DerefMut, IntoIterator};
4
5use indexmap::IndexMap;
6use serde::{Deserialize, Serialize};
7
8use crate::utils::{get_key, parse_hs_key, To01String};
9use crate::{
10 errors::{VmfError, VmfResult},
11 VmfBlock, VmfSerializable,
12};
13
14#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
16pub struct VersionInfo {
17 pub editor_version: i32,
19 pub editor_build: i32,
21 pub map_version: i32,
23 pub format_version: i32,
25 pub prefab: bool,
27}
28
29impl TryFrom<VmfBlock> for VersionInfo {
30 type Error = VmfError;
31
32 fn try_from(block: VmfBlock) -> VmfResult<Self> {
33 let kv = &block.key_values;
34 Ok(Self {
35 editor_version: parse_hs_key!(kv, "editorversion", i32)?,
36 editor_build: parse_hs_key!(kv, "editorbuild", i32)?,
37 map_version: parse_hs_key!(kv, "mapversion", i32)?,
38 format_version: parse_hs_key!(kv, "formatversion", i32)?,
39 prefab: get_key!(kv, "prefab")? == "1",
40 })
41 }
42}
43
44impl From<VersionInfo> for VmfBlock {
45 fn from(val: VersionInfo) -> Self {
46 let mut key_values = IndexMap::new();
47 key_values.insert("editorversion".to_string(), val.editor_version.to_string());
48 key_values.insert("editorbuild".to_string(), val.editor_build.to_string());
49 key_values.insert("mapversion".to_string(), val.map_version.to_string());
50 key_values.insert("formatversion".to_string(), val.format_version.to_string());
51 key_values.insert("prefab".to_string(), val.prefab.to_01_string());
52
53 VmfBlock {
54 name: "versioninfo".to_string(),
55 key_values,
56 blocks: Vec::new(),
57 }
58 }
59}
60
61impl VmfSerializable for VersionInfo {
62 fn to_vmf_string(&self, indent_level: usize) -> String {
63 let indent = "\t".repeat(indent_level);
64 let mut output = String::with_capacity(256);
65
66 output.push_str(&format!("{0}versioninfo\n{0}{{\n", indent));
67 output.push_str(&format!(
68 "{}\t\"editorversion\" \"{}\"\n",
69 indent, self.editor_version
70 ));
71 output.push_str(&format!(
72 "{}\t\"editorbuild\" \"{}\"\n",
73 indent, self.editor_build
74 ));
75 output.push_str(&format!(
76 "{}\t\"mapversion\" \"{}\"\n",
77 indent, self.map_version
78 ));
79 output.push_str(&format!(
80 "{}\t\"formatversion\" \"{}\"\n",
81 indent, self.format_version
82 ));
83 output.push_str(&format!(
84 "{}\t\"prefab\" \"{}\"\n",
85 indent,
86 self.prefab.to_01_string()
87 ));
88
89 output.push_str(&format!("{}}}\n", indent));
90 output
91 }
92}
93
94#[derive(
96 Debug, Default, Clone, Serialize, Deserialize, PartialEq, Deref, DerefMut, IntoIterator,
97)]
98pub struct VisGroups {
99 #[deref]
101 pub groups: Vec<VisGroup>,
102}
103
104impl TryFrom<VmfBlock> for VisGroups {
105 type Error = VmfError;
106
107 fn try_from(block: VmfBlock) -> VmfResult<Self> {
108 let mut groups = Vec::with_capacity(12);
109 for group in block.blocks {
110 groups.push(VisGroup::try_from(group)?);
111 }
112
113 Ok(Self { groups })
114 }
115}
116
117impl From<VisGroups> for VmfBlock {
118 fn from(val: VisGroups) -> Self {
119 let mut visgroups_block = VmfBlock {
120 name: "visgroups".to_string(),
121 key_values: IndexMap::new(),
122 blocks: Vec::with_capacity(val.groups.len()),
123 };
124
125 for group in val.groups {
126 visgroups_block.blocks.push(group.into())
127 }
128
129 visgroups_block
130 }
131}
132
133impl VmfSerializable for VisGroups {
134 fn to_vmf_string(&self, indent_level: usize) -> String {
135 let indent = "\t".repeat(indent_level);
136 let mut output = String::with_capacity(128);
137
138 output.push_str(&format!("{0}visgroups\n{0}{{\n", indent));
139
140 if self.groups.is_empty() {
141 output.push_str(&format!("{}}}\n", indent));
142 return output;
143 }
144
145 for group in &self.groups {
146 output.push_str(&group.to_vmf_string(indent_level));
147 }
148
149 output.push_str(&format!("{}}}\n", indent));
150 output
151 }
152}
153
154#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
156pub struct VisGroup {
157 pub name: String,
159 pub id: i32,
161 pub color: String,
163 #[serde(default, skip_serializing_if = "Option::is_none")]
165 pub children: Option<Vec<VisGroup>>,
166}
167
168impl TryFrom<VmfBlock> for VisGroup {
169 type Error = VmfError;
170
171 fn try_from(block: VmfBlock) -> VmfResult<Self> {
172 let children = if block.blocks.is_empty() {
173 None
174 } else {
175 Some(
176 block
177 .blocks
178 .into_iter()
179 .map(VisGroup::try_from)
180 .collect::<VmfResult<Vec<_>>>()?,
181 )
182 };
183
184 Ok(Self {
185 name: get_key!(block.key_values, "name")?.to_owned(),
186 id: parse_hs_key!(block.key_values, "visgroupid", i32)?,
187 color: get_key!(block.key_values, "color")?.to_owned(),
188 children,
189 })
190 }
191}
192
193impl From<VisGroup> for VmfBlock {
194 fn from(val: VisGroup) -> Self {
195 let mut visgroup_block = VmfBlock {
197 name: "visgroup".to_string(),
198 key_values: IndexMap::new(),
199 blocks: Vec::new(),
200 };
201
202 visgroup_block
204 .key_values
205 .insert("name".to_string(), val.name);
206 visgroup_block
207 .key_values
208 .insert("visgroupid".to_string(), val.id.to_string());
209 visgroup_block
210 .key_values
211 .insert("color".to_string(), val.color);
212
213 if let Some(children) = val.children {
215 for child in children {
216 visgroup_block.blocks.push(child.into());
217 }
218 }
219
220 visgroup_block
221 }
222}
223
224impl VmfSerializable for VisGroup {
225 fn to_vmf_string(&self, indent_level: usize) -> String {
226 let indent = "\t".repeat(indent_level);
227 let mut output = String::with_capacity(64);
228
229 output.push_str(&format!("{0}\tvisgroup\n\t{0}{{\n", indent));
230 output.push_str(&format!("{}\t\t\"name\" \"{}\"\n", indent, self.name));
231 output.push_str(&format!("{}\t\t\"visgroupid\" \"{}\"\n", indent, self.id));
232 output.push_str(&format!("{}\t\t\"color\" \"{}\"\n", indent, self.color));
233
234 if let Some(ref children) = self.children {
236 for child in children {
237 output.push_str(&child.to_vmf_string(indent_level + 1));
238 }
239 }
240
241 output.push_str(&format!("{}\t}}\n", indent));
242 output
243 }
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
248pub struct ViewSettings {
249 pub snap_to_grid: bool,
251 pub show_grid: bool,
253 pub show_logical_grid: bool,
255 pub grid_spacing: u16,
257 pub show_3d_grid: bool,
259}
260
261impl Default for ViewSettings {
262 fn default() -> Self {
263 Self {
264 snap_to_grid: true,
265 show_grid: true,
266 show_logical_grid: false,
267 grid_spacing: 8,
268 show_3d_grid: false,
269 }
270 }
271}
272
273impl TryFrom<VmfBlock> for ViewSettings {
274 type Error = VmfError;
275
276 fn try_from(block: VmfBlock) -> VmfResult<Self> {
277 Ok(Self {
278 snap_to_grid: get_key!(&block.key_values, "bSnapToGrid")? == "1",
279 show_grid: get_key!(&block.key_values, "bShowGrid")? == "1",
280 show_logical_grid: get_key!(&block.key_values, "bShowLogicalGrid")? == "1",
281 grid_spacing: get_key!(&block.key_values, "nGridSpacing")?
282 .parse()
283 .unwrap_or(64),
284 show_3d_grid: get_key!(&block.key_values, "bShow3DGrid", "0".into()) == "1",
285 })
286 }
287}
288
289impl From<ViewSettings> for VmfBlock {
290 fn from(val: ViewSettings) -> Self {
291 let mut key_values = IndexMap::new();
292 key_values.insert("bSnapToGrid".to_string(), val.snap_to_grid.to_01_string());
293 key_values.insert("bShowGrid".to_string(), val.show_grid.to_01_string());
294 key_values.insert(
295 "bShowLogicalGrid".to_string(),
296 val.show_logical_grid.to_01_string(),
297 );
298 key_values.insert("nGridSpacing".to_string(), val.grid_spacing.to_string());
299 key_values.insert("bShow3DGrid".to_string(), val.show_3d_grid.to_01_string());
300
301 VmfBlock {
302 name: "viewsettings".to_string(),
303 key_values,
304 blocks: Vec::new(),
305 }
306 }
307}
308
309impl VmfSerializable for ViewSettings {
310 fn to_vmf_string(&self, indent_level: usize) -> String {
311 let indent = "\t".repeat(indent_level);
312 let mut output = String::with_capacity(64);
313
314 output.push_str(&format!("{0}viewsettings\n{0}{{\n", indent));
315 output.push_str(&format!(
316 "{}\t\"bSnapToGrid\" \"{}\"\n",
317 indent,
318 self.snap_to_grid.to_01_string()
319 ));
320 output.push_str(&format!(
321 "{}\t\"bShowGrid\" \"{}\"\n",
322 indent,
323 self.show_grid.to_01_string()
324 ));
325 output.push_str(&format!(
326 "{}\t\"bShowLogicalGrid\" \"{}\"\n",
327 indent,
328 self.show_logical_grid.to_01_string()
329 ));
330 output.push_str(&format!(
331 "{}\t\"nGridSpacing\" \"{}\"\n",
332 indent, self.grid_spacing
333 ));
334 output.push_str(&format!(
335 "{}\t\"bShow3DGrid\" \"{}\"\n",
336 indent,
337 self.show_3d_grid.to_01_string()
338 ));
339
340 output.push_str(&format!("{}}}\n", indent));
341 output
342 }
343}