1use derive_more::{Deref, DerefMut, IntoIterator};
4
5use indexmap::IndexMap;
6#[cfg(feature = "serialization")]
7use serde::{Deserialize, Serialize};
8
9use crate::utils::{To01String, get_key_ref, take_and_parse_key, take_key_owned};
10use crate::{
11 VmfBlock, VmfSerializable,
12 errors::{VmfError, VmfResult},
13};
14
15#[derive(Debug, Default, Clone, PartialEq)]
17#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
18pub struct VersionInfo {
19 pub editor_version: i32,
21 pub editor_build: i32,
23 pub map_version: i32,
25 pub format_version: i32,
27 pub prefab: bool,
29}
30
31impl TryFrom<VmfBlock> for VersionInfo {
32 type Error = VmfError;
33
34 fn try_from(mut block: VmfBlock) -> VmfResult<Self> {
35 let kv = &mut block.key_values;
36 Ok(Self {
37 editor_version: take_and_parse_key::<i32>(kv, "editorversion")?,
38 editor_build: take_and_parse_key::<i32>(kv, "editorbuild")?,
39 map_version: take_and_parse_key::<i32>(kv, "mapversion")?,
40 format_version: take_and_parse_key::<i32>(kv, "formatversion")?,
41 prefab: get_key_ref(kv, "prefab")? == "1",
42 })
43 }
44}
45
46impl From<VersionInfo> for VmfBlock {
47 fn from(val: VersionInfo) -> Self {
48 let mut key_values = IndexMap::new();
49 key_values.insert("editorversion".to_string(), val.editor_version.to_string());
50 key_values.insert("editorbuild".to_string(), val.editor_build.to_string());
51 key_values.insert("mapversion".to_string(), val.map_version.to_string());
52 key_values.insert("formatversion".to_string(), val.format_version.to_string());
53 key_values.insert("prefab".to_string(), val.prefab.to_01_string());
54
55 VmfBlock {
56 name: "versioninfo".to_string(),
57 key_values,
58 blocks: Vec::new(),
59 }
60 }
61}
62
63impl VmfSerializable for VersionInfo {
64 fn to_vmf_string(&self, indent_level: usize) -> String {
65 let indent = "\t".repeat(indent_level);
66 let mut output = String::with_capacity(256);
67
68 output.push_str(&format!("{0}versioninfo\n{0}{{\n", indent));
69 output.push_str(&format!(
70 "{}\t\"editorversion\" \"{}\"\n",
71 indent, self.editor_version
72 ));
73 output.push_str(&format!(
74 "{}\t\"editorbuild\" \"{}\"\n",
75 indent, self.editor_build
76 ));
77 output.push_str(&format!(
78 "{}\t\"mapversion\" \"{}\"\n",
79 indent, self.map_version
80 ));
81 output.push_str(&format!(
82 "{}\t\"formatversion\" \"{}\"\n",
83 indent, self.format_version
84 ));
85 output.push_str(&format!(
86 "{}\t\"prefab\" \"{}\"\n",
87 indent,
88 self.prefab.to_01_string()
89 ));
90
91 output.push_str(&format!("{}}}\n", indent));
92 output
93 }
94}
95
96#[derive(Debug, Default, Clone, PartialEq, Deref, DerefMut, IntoIterator)]
98#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
99pub struct VisGroups {
100 #[deref]
102 pub groups: Vec<VisGroup>,
103}
104
105fn find_visgroup_by_id(groups: &[VisGroup], id_to_find: i32) -> Option<&VisGroup> {
108 for group in groups {
109 if group.id == id_to_find {
110 return Some(group);
111 }
112 if let Some(ref children) = group.children {
113 if let Some(found) = find_visgroup_by_id(children, id_to_find) {
114 return Some(found);
115 }
116 }
117 }
118 None
119}
120
121fn find_visgroup_by_id_mut(
124 groups: &mut [VisGroup],
125 id_to_find: i32,
126) -> Option<&mut VisGroup> {
127 for group in groups {
128 if group.id == id_to_find {
129 return Some(group);
130 }
131 if let Some(ref mut children) = group.children {
132 if let Some(found) = find_visgroup_by_id_mut(children, id_to_find) {
133 return Some(found);
134 }
135 }
136 }
137 None
138}
139
140fn find_visgroup_by_name<'a>(groups: &'a [VisGroup], name_to_find: &str) -> Option<&'a VisGroup> {
143 for group in groups {
144 if group.name == name_to_find {
145 return Some(group);
146 }
147 if let Some(ref children) = group.children {
148 if let Some(found) = find_visgroup_by_name(children, name_to_find) {
149 return Some(found);
150 }
151 }
152 }
153 None
154}
155
156fn find_visgroup_by_name_mut<'a>(
159 groups: &'a mut [VisGroup],
160 name_to_find: &str,
161) -> Option<&'a mut VisGroup> {
162 for group in groups {
163 if group.name == name_to_find {
164 return Some(group);
165 }
166 if let Some(ref mut children) = group.children {
167 if let Some(found) = find_visgroup_by_name_mut(children, name_to_find) {
168 return Some(found);
169 }
170 }
171 }
172 None
173}
174
175impl VisGroups {
176 pub fn find_by_name(&self, name: &str) -> Option<&VisGroup> {
186 find_visgroup_by_name(&self.groups, name)
187 }
188
189 pub fn find_by_name_mut(&mut self, name: &str) -> Option<&mut VisGroup> {
199 find_visgroup_by_name_mut(&mut self.groups, name)
200 }
201
202 pub fn find_by_id(&self, id: i32) -> Option<&VisGroup> {
212 find_visgroup_by_id(&self.groups, id)
213 }
214
215 pub fn find_by_id_mut(&mut self, id: i32) -> Option<&mut VisGroup> {
225 find_visgroup_by_id_mut(&mut self.groups, id)
226 }
227}
228
229impl TryFrom<VmfBlock> for VisGroups {
230 type Error = VmfError;
231
232 fn try_from(block: VmfBlock) -> VmfResult<Self> {
233 let mut groups = Vec::with_capacity(block.blocks.len());
234 for group in block.blocks {
235 groups.push(VisGroup::try_from(group)?);
236 }
237
238 Ok(Self { groups })
239 }
240}
241
242impl From<VisGroups> for VmfBlock {
243 fn from(val: VisGroups) -> Self {
244 let mut visgroups_block = VmfBlock {
245 name: "visgroups".to_string(),
246 key_values: IndexMap::new(),
247 blocks: Vec::with_capacity(val.groups.len()),
248 };
249
250 for group in val.groups {
251 visgroups_block.blocks.push(group.into())
252 }
253
254 visgroups_block
255 }
256}
257
258impl VmfSerializable for VisGroups {
259 fn to_vmf_string(&self, indent_level: usize) -> String {
260 let indent = "\t".repeat(indent_level);
261 let mut output = String::with_capacity(128);
262
263 output.push_str(&format!("{0}visgroups\n{0}{{\n", indent));
264
265 if self.groups.is_empty() {
266 output.push_str(&format!("{}}}\n", indent));
267 return output;
268 }
269
270 for group in &self.groups {
271 output.push_str(&group.to_vmf_string(indent_level));
272 }
273
274 output.push_str(&format!("{}}}\n", indent));
275 output
276 }
277}
278
279#[derive(Debug, Default, Clone, PartialEq)]
281#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
282pub struct VisGroup {
283 pub name: String,
285 pub id: i32,
287 pub color: String,
289 #[cfg_attr(
291 feature = "serialization",
292 serde(default, skip_serializing_if = "Option::is_none")
293 )]
294 pub children: Option<Vec<VisGroup>>,
295}
296
297impl TryFrom<VmfBlock> for VisGroup {
298 type Error = VmfError;
299
300 fn try_from(mut block: VmfBlock) -> VmfResult<Self> {
301 let children = if !block.blocks.is_empty() {
302 let mut children_vec = Vec::with_capacity(block.blocks.len());
303 for child_block in block.blocks {
304 children_vec.push(VisGroup::try_from(child_block)?);
305 }
306 Some(children_vec)
307 } else {
308 None
309 };
310
311 let kv = &mut block.key_values;
312 Ok(Self {
313 name: take_key_owned(kv, "name")?,
314 id: take_and_parse_key::<i32>(kv, "visgroupid")?,
315 color: take_key_owned(kv, "color")?,
316 children,
317 })
318 }
319}
320
321impl From<VisGroup> for VmfBlock {
322 fn from(val: VisGroup) -> Self {
323 let mut visgroup_block = VmfBlock {
325 name: "visgroup".to_string(),
326 key_values: IndexMap::new(),
327 blocks: Vec::new(),
328 };
329
330 visgroup_block
332 .key_values
333 .insert("name".to_string(), val.name);
334 visgroup_block
335 .key_values
336 .insert("visgroupid".to_string(), val.id.to_string());
337 visgroup_block
338 .key_values
339 .insert("color".to_string(), val.color);
340
341 if let Some(children) = val.children {
343 for child in children {
344 visgroup_block.blocks.push(child.into());
345 }
346 }
347
348 visgroup_block
349 }
350}
351
352impl VmfSerializable for VisGroup {
353 fn to_vmf_string(&self, indent_level: usize) -> String {
354 let indent = "\t".repeat(indent_level);
355 let mut output = String::with_capacity(64);
356
357 output.push_str(&format!("{0}\tvisgroup\n\t{0}{{\n", indent));
358 output.push_str(&format!("{}\t\t\"name\" \"{}\"\n", indent, self.name));
359 output.push_str(&format!("{}\t\t\"visgroupid\" \"{}\"\n", indent, self.id));
360 output.push_str(&format!("{}\t\t\"color\" \"{}\"\n", indent, self.color));
361
362 if let Some(ref children) = self.children {
364 for child in children {
365 output.push_str(&child.to_vmf_string(indent_level + 1));
366 }
367 }
368
369 output.push_str(&format!("{}\t}}\n", indent));
370 output
371 }
372}
373
374#[derive(Debug, Clone, PartialEq)]
376#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
377pub struct ViewSettings {
378 pub snap_to_grid: bool,
380 pub show_grid: bool,
382 pub show_logical_grid: bool,
384 pub grid_spacing: u16,
386 pub show_3d_grid: bool,
388}
389
390impl Default for ViewSettings {
391 fn default() -> Self {
392 Self {
393 snap_to_grid: true,
394 show_grid: true,
395 show_logical_grid: false,
396 grid_spacing: 8,
397 show_3d_grid: false,
398 }
399 }
400}
401
402impl TryFrom<VmfBlock> for ViewSettings {
403 type Error = VmfError;
404
405 fn try_from(mut block: VmfBlock) -> VmfResult<Self> {
406 let kv = &mut block.key_values;
407
408 Ok(Self {
409 snap_to_grid: get_key_ref(kv, "bSnapToGrid")? == "1",
410 show_grid: get_key_ref(kv, "bShowGrid")? == "1",
411 show_logical_grid: get_key_ref(kv, "bShowLogicalGrid")? == "1",
412 grid_spacing: take_and_parse_key::<u16>(kv, "nGridSpacing").unwrap_or(64),
413 show_3d_grid: get_key_ref(kv, "bShow3DGrid").map_or("0", |v| v) == "1",
414 })
415 }
416}
417
418impl From<ViewSettings> for VmfBlock {
419 fn from(val: ViewSettings) -> Self {
420 let mut key_values = IndexMap::new();
421 key_values.insert("bSnapToGrid".to_string(), val.snap_to_grid.to_01_string());
422 key_values.insert("bShowGrid".to_string(), val.show_grid.to_01_string());
423 key_values.insert(
424 "bShowLogicalGrid".to_string(),
425 val.show_logical_grid.to_01_string(),
426 );
427 key_values.insert("nGridSpacing".to_string(), val.grid_spacing.to_string());
428 key_values.insert("bShow3DGrid".to_string(), val.show_3d_grid.to_01_string());
429
430 VmfBlock {
431 name: "viewsettings".to_string(),
432 key_values,
433 blocks: Vec::new(),
434 }
435 }
436}
437
438impl VmfSerializable for ViewSettings {
439 fn to_vmf_string(&self, indent_level: usize) -> String {
440 let indent = "\t".repeat(indent_level);
441 let mut output = String::with_capacity(64);
442
443 output.push_str(&format!("{0}viewsettings\n{0}{{\n", indent));
444 output.push_str(&format!(
445 "{}\t\"bSnapToGrid\" \"{}\"\n",
446 indent,
447 self.snap_to_grid.to_01_string()
448 ));
449 output.push_str(&format!(
450 "{}\t\"bShowGrid\" \"{}\"\n",
451 indent,
452 self.show_grid.to_01_string()
453 ));
454 output.push_str(&format!(
455 "{}\t\"bShowLogicalGrid\" \"{}\"\n",
456 indent,
457 self.show_logical_grid.to_01_string()
458 ));
459 output.push_str(&format!(
460 "{}\t\"nGridSpacing\" \"{}\"\n",
461 indent, self.grid_spacing
462 ));
463 output.push_str(&format!(
464 "{}\t\"bShow3DGrid\" \"{}\"\n",
465 indent,
466 self.show_3d_grid.to_01_string()
467 ));
468
469 output.push_str(&format!("{}}}\n", indent));
470 output
471 }
472}