1use indexmap::IndexMap;
4#[cfg(feature = "serialization")]
5use serde::{Deserialize, Serialize};
6
7use super::common::Editor;
8use crate::utils::{To01String, get_key_ref, take_and_parse_key, take_key_owned};
9use crate::{
10 VmfBlock, VmfSerializable,
11 errors::{VmfError, VmfResult},
12};
13use std::mem;
14
15#[derive(Debug, Default, Clone, PartialEq)]
17#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
18pub struct World {
19 pub key_values: IndexMap<String, String>,
21 pub solids: Vec<Solid>,
23 pub hidden: Vec<Solid>,
25 #[cfg_attr(
27 feature = "serialization",
28 serde(default, skip_serializing_if = "Option::is_none")
29 )]
30 pub group: Option<Group>,
31}
32
33impl TryFrom<VmfBlock> for World {
34 type Error = VmfError;
35
36 fn try_from(block: VmfBlock) -> VmfResult<Self> {
37 let estimated_solids = block.blocks.len().saturating_sub(1);
38 let mut world = World {
39 key_values: block.key_values,
40 solids: Vec::with_capacity(estimated_solids),
41 hidden: Vec::with_capacity(16),
42 group: None,
43 };
44
45 for mut inner_block in block.blocks {
46 match inner_block.name.as_str() {
47 "solid" => world.solids.push(Solid::try_from(inner_block)?),
48 "group" => world.group = Group::try_from(inner_block).ok(),
49 "hidden" => {
50 if !inner_block.blocks.is_empty() {
51 let hidden_block = mem::take(&mut inner_block.blocks[0]);
53 world.hidden.push(Solid::try_from(hidden_block)?);
54 }
55 }
56 _ => {
57 #[cfg(feature = "debug_assert_info")]
59 debug_assert!(false, "Unexpected block name: {}", inner_block.name);
60 }
61 };
62 }
63
64 Ok(world)
65 }
66}
67
68impl From<World> for VmfBlock {
69 fn from(val: World) -> Self {
70 let mut blocks = Vec::new();
71
72 for solid in val.solids {
74 blocks.push(solid.into());
75 }
76
77 for hidden_solid in val.hidden {
79 blocks.push(VmfBlock {
80 name: "hidden".to_string(),
81 key_values: IndexMap::new(),
82 blocks: vec![hidden_solid.into()],
83 });
84 }
85
86 if let Some(group) = val.group {
88 blocks.push(group.into());
89 }
90
91 VmfBlock {
92 name: "world".to_string(),
93 key_values: val.key_values,
94 blocks,
95 }
96 }
97}
98
99impl VmfSerializable for World {
100 fn to_vmf_string(&self, indent_level: usize) -> String {
101 let indent = "\t".repeat(indent_level);
102 let mut output = String::with_capacity(2048);
103
104 output.push_str(&format!("{0}world\n{0}{{\n", indent));
105
106 for (key, value) in &self.key_values {
108 output.push_str(&format!("{}\t\"{}\" \"{}\"\n", indent, key, value));
109 }
110
111 if !self.solids.is_empty() {
113 for solid in &self.solids {
114 output.push_str(&solid.to_vmf_string(indent_level + 1));
115 }
116 }
117
118 if !self.hidden.is_empty() {
120 output.push_str(&format!("{0}\tHidden\n{0}\t{{\n", indent));
121 for solid in &self.hidden {
122 output.push_str(&solid.to_vmf_string(indent_level + 2));
123 }
124 output.push_str(&format!("{}\t}}\n", indent));
125 }
126
127 if let Some(group) = &self.group {
129 output.push_str(&group.to_vmf_string(indent_level + 1));
130 }
131
132 output.push_str(&format!("{}}}\n", indent));
133 output
134 }
135}
136
137#[derive(Debug, Default, Clone, PartialEq)]
139#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
140pub struct Solid {
141 pub id: u64,
143 pub sides: Vec<Side>,
145 pub editor: Editor,
147}
148
149impl TryFrom<VmfBlock> for Solid {
150 type Error = VmfError;
151
152 fn try_from(mut block: VmfBlock) -> VmfResult<Self> {
153 let mut solid = Solid {
154 id: take_and_parse_key::<u64>(&mut block.key_values, "id")?,
155 sides: Vec::with_capacity(block.blocks.len()),
156 ..Default::default()
157 };
158
159 for inner_block in block.blocks {
160 match inner_block.name.as_str() {
161 "side" => solid.sides.push(Side::try_from(inner_block)?),
162 "editor" => solid.editor = Editor::try_from(inner_block)?,
163 _ => {
164 #[cfg(feature = "debug_assert_info")]
165 debug_assert!(false, "Unexpected block name: {}", inner_block.name);
166 }
167 }
168 }
169
170 Ok(solid)
171 }
172}
173
174impl From<Solid> for VmfBlock {
175 fn from(val: Solid) -> Self {
176 let mut blocks = Vec::new();
177
178 for side in val.sides {
180 blocks.push(side.into());
181 }
182
183 blocks.push(val.editor.into());
185
186 VmfBlock {
187 name: "solid".to_string(),
188 key_values: {
189 let mut key_values = IndexMap::new();
190 key_values.insert("id".to_string(), val.id.to_string());
191 key_values
192 },
193 blocks,
194 }
195 }
196}
197
198impl VmfSerializable for Solid {
199 fn to_vmf_string(&self, indent_level: usize) -> String {
200 let indent = "\t".repeat(indent_level);
201 let mut output = String::with_capacity(256);
202
203 output.push_str(&format!("{0}solid\n{0}{{\n", indent));
205 output.push_str(&format!("{}\t\"id\" \"{}\"\n", indent, self.id));
206
207 for side in &self.sides {
209 output.push_str(&side.to_vmf_string(indent_level + 1));
210 }
211
212 output.push_str(&self.editor.to_vmf_string(indent_level + 1));
214
215 output.push_str(&format!("{}}}\n", indent));
216
217 output
218 }
219}
220
221#[derive(Debug, Default, Clone, PartialEq)]
223#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
224pub struct Side {
225 pub id: u32,
227 pub plane: String,
229 pub material: String,
231 pub u_axis: String,
233 pub v_axis: String,
235 #[cfg_attr(
237 feature = "serialization",
238 serde(default, skip_serializing_if = "Option::is_none")
239 )]
240 pub rotation: Option<f32>,
241 pub lightmap_scale: u16,
243 pub smoothing_groups: i32,
245 #[cfg_attr(
247 feature = "serialization",
248 serde(default, skip_serializing_if = "Option::is_none")
249 )]
250 pub flags: Option<u32>,
251 #[cfg_attr(
253 feature = "serialization",
254 serde(default, skip_serializing_if = "Option::is_none")
255 )]
256 pub dispinfo: Option<DispInfo>,
257}
258
259impl TryFrom<VmfBlock> for Side {
260 type Error = VmfError;
261
262 fn try_from(mut block: VmfBlock) -> VmfResult<Self> {
263 let kv = &mut block.key_values;
264 let dispinfo_block = block.blocks.iter_mut().find(|b| b.name == "dispinfo");
267
268 let plane = take_key_owned(kv, "plane")?;
270 let material = take_key_owned(kv, "material")?;
271 let u_axis = take_key_owned(kv, "uaxis")?;
272 let v_axis = take_key_owned(kv, "vaxis")?;
273
274 let id = take_and_parse_key::<u32>(kv, "id")?;
276 let lightmap_scale = take_and_parse_key::<u16>(kv, "lightmapscale")?;
277 let smoothing_groups = take_and_parse_key::<i32>(kv, "smoothing_groups")?;
278
279 let rotation = take_and_parse_key::<f32>(kv, "rotation").ok();
282 let flags = take_and_parse_key::<u32>(kv, "flags").ok();
283
284 let dispinfo = match dispinfo_block {
285 Some(block) => Some(DispInfo::try_from(mem::take(block))?),
286 None => None,
287 };
288
289 Ok(Side {
290 id,
291 plane,
292 material,
293 u_axis,
294 v_axis,
295 rotation,
296 lightmap_scale,
297 smoothing_groups,
298 flags,
299 dispinfo,
300 })
301 }
302}
303
304impl From<Side> for VmfBlock {
305 fn from(val: Side) -> Self {
306 let mut key_values = IndexMap::new();
307 key_values.insert("id".to_string(), val.id.to_string());
308 key_values.insert("plane".to_string(), val.plane);
309 key_values.insert("material".to_string(), val.material);
310 key_values.insert("uaxis".to_string(), val.u_axis);
311 key_values.insert("vaxis".to_string(), val.v_axis);
312 key_values.insert("lightmapscale".to_string(), val.lightmap_scale.to_string());
313 key_values.insert(
314 "smoothing_groups".to_string(),
315 val.smoothing_groups.to_string(),
316 );
317
318 if let Some(rotation) = val.rotation {
319 key_values.insert("rotation".to_string(), rotation.to_string());
320 }
321 if let Some(flags) = val.flags {
322 key_values.insert("flags".to_string(), flags.to_string());
323 }
324
325 let mut blocks = Vec::new();
326 if let Some(dispinfo) = val.dispinfo {
327 blocks.push(dispinfo.into());
328 }
329
330 VmfBlock {
331 name: "side".to_string(),
332 key_values,
333 blocks,
334 }
335 }
336}
337
338impl VmfSerializable for Side {
339 fn to_vmf_string(&self, indent_level: usize) -> String {
340 let indent = "\t".repeat(indent_level);
341 let mut output = String::with_capacity(256);
342
343 output.push_str(&format!("{0}side\n{0}{{\n", indent));
345
346 output.push_str(&format!("{}\t\"id\" \"{}\"\n", indent, self.id));
348 output.push_str(&format!("{}\t\"plane\" \"{}\"\n", indent, self.plane));
349 output.push_str(&format!("{}\t\"material\" \"{}\"\n", indent, self.material));
350 output.push_str(&format!("{}\t\"uaxis\" \"{}\"\n", indent, self.u_axis));
351 output.push_str(&format!("{}\t\"vaxis\" \"{}\"\n", indent, self.v_axis));
352
353 if let Some(rotation) = self.rotation {
354 output.push_str(&format!("{}\t\"rotation\" \"{}\"\n", indent, rotation));
355 }
356
357 output.push_str(&format!(
358 "{}\t\"lightmapscale\" \"{}\"\n",
359 indent, self.lightmap_scale
360 ));
361 output.push_str(&format!(
362 "{}\t\"smoothing_groups\" \"{}\"\n",
363 indent, self.smoothing_groups
364 ));
365
366 if let Some(flags) = self.flags {
368 output.push_str(&format!("{}\t\"flags\" \"{}\"\n", indent, flags));
369 }
370
371 if let Some(dispinfo) = &self.dispinfo {
372 output.push_str(&dispinfo.to_vmf_string(indent_level + 1));
373 }
374
375 output.push_str(&format!("{0}}}\n", indent));
377
378 output
379 }
380}
381
382#[inline(always)]
396fn take_block(blocks: &mut Vec<VmfBlock>, name: &str) -> VmfResult<VmfBlock> {
397 let index = blocks.iter().position(|b| b.name == name);
398 match index {
399 Some(idx) => Ok(blocks.swap_remove(idx)),
401 None => Err(VmfError::InvalidFormat(format!(
402 "Missing {} block in dispinfo",
403 name
404 ))),
405 }
406}
407
408#[derive(Debug, Default, Clone, PartialEq)]
410#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
411pub struct DispInfo {
412 pub power: u8,
414 pub start_position: String,
416 #[cfg_attr(
418 feature = "serialization",
419 serde(default, skip_serializing_if = "Option::is_none")
420 )]
421 pub flags: Option<u32>,
422 pub elevation: f32,
424 pub subdiv: bool,
426 pub normals: DispRows,
428 pub distances: DispRows,
430 pub offsets: DispRows,
432 pub offset_normals: DispRows,
434 pub alphas: DispRows,
436 pub triangle_tags: DispRows,
438 pub allowed_verts: IndexMap<String, Vec<i32>>,
440}
441
442impl TryFrom<VmfBlock> for DispInfo {
443 type Error = VmfError;
444
445 fn try_from(mut block: VmfBlock) -> VmfResult<Self> {
446 let normals_block = take_block(&mut block.blocks, "normals")?;
448 let distances_block = take_block(&mut block.blocks, "distances")?;
449 let alphas_block = take_block(&mut block.blocks, "alphas")?;
450 let triangle_tags_block = take_block(&mut block.blocks, "triangle_tags")?;
451 let allowed_verts_block = take_block(&mut block.blocks, "allowed_verts")?;
452
453 let offsets = block
455 .blocks
456 .iter_mut()
457 .find(|b| b.name == "offsets")
458 .map_or_else(
459 || Ok(DispRows::default()),
460 |b| DispRows::try_from(mem::take(b)),
461 )?;
462
463 let offset_normals = block
464 .blocks
465 .iter_mut()
466 .find(|b| b.name == "offset_normals")
467 .map_or_else(
468 || Ok(DispRows::default()),
469 |b| DispRows::try_from(mem::take(b)),
470 )?;
471
472 let kv = &mut block.key_values;
474 let power = take_and_parse_key::<u8>(kv, "power")?;
475 let start_position = take_key_owned(kv, "startposition")?;
476 let flags = take_and_parse_key::<u32>(kv, "flags").ok();
477 let elevation = take_and_parse_key::<f32>(kv, "elevation")?;
478 let subdiv = get_key_ref(kv, "subdiv")? == "1";
479
480 let normals = DispRows::try_from(normals_block)?;
482 let distances = DispRows::try_from(distances_block)?;
483 let alphas = DispRows::try_from(alphas_block)?;
484 let triangle_tags = DispRows::try_from(triangle_tags_block)?;
485 let allowed_verts = DispInfo::parse_allowed_verts(allowed_verts_block)?;
486
487 Ok(DispInfo {
488 power,
489 start_position,
490 flags,
491 elevation,
492 subdiv,
493 normals,
494 distances,
495 offsets,
496 offset_normals,
497 alphas,
498 triangle_tags,
499 allowed_verts,
500 })
501 }
502}
503
504impl From<DispInfo> for VmfBlock {
505 fn from(val: DispInfo) -> Self {
506 let blocks = vec![
507 val.normals.into_vmf_block("normals"),
508 val.distances.into_vmf_block("distances"),
509 val.offsets.into_vmf_block("offsets"),
510 val.offset_normals.into_vmf_block("offset_normals"),
511 val.alphas.into_vmf_block("alphas"),
512 val.triangle_tags.into_vmf_block("triangle_tags"),
513 DispInfo::allowed_verts_into_vmf_block(val.allowed_verts),
514 ];
515
516 let mut key_values = IndexMap::new();
517 key_values.insert("power".to_string(), val.power.to_string());
518 key_values.insert("startposition".to_string(), val.start_position);
519 key_values.insert("elevation".to_string(), val.elevation.to_string());
520 key_values.insert("subdiv".to_string(), val.subdiv.to_01_string());
521
522 if let Some(flags) = val.flags {
523 key_values.insert("flags".to_string(), flags.to_string());
524 }
525
526 VmfBlock {
527 name: "dispinfo".to_string(),
528 key_values,
529 blocks,
530 }
531 }
532}
533
534impl VmfSerializable for DispInfo {
535 fn to_vmf_string(&self, indent_level: usize) -> String {
536 let indent = "\t".repeat(indent_level);
537 let mut output = String::with_capacity(256);
538
539 output.push_str(&format!("{}dispinfo\n", indent));
540 output.push_str(&format!("{}{{\n", indent));
541 output.push_str(&format!("{}\t\"power\" \"{}\"\n", indent, self.power));
542 output.push_str(&format!(
543 "{}\t\"startposition\" \"{}\"\n",
544 indent, self.start_position
545 ));
546
547 if let Some(flags) = self.flags {
549 output.push_str(&format!("{}\t\"flags\" \"{}\"\n", indent, flags));
550 }
551
552 output.push_str(&format!(
553 "{}\t\"elevation\" \"{}\"\n",
554 indent, self.elevation
555 ));
556 output.push_str(&format!(
557 "{}\t\"subdiv\" \"{}\"\n",
558 indent,
559 self.subdiv.to_01_string()
560 ));
561 output.push_str(&self.normals.to_vmf_string(indent_level + 1, "normals"));
562 output.push_str(&self.distances.to_vmf_string(indent_level + 1, "distances"));
563 output.push_str(&self.offsets.to_vmf_string(indent_level + 1, "offsets"));
564 output.push_str(
565 &self
566 .offset_normals
567 .to_vmf_string(indent_level + 1, "offset_normals"),
568 );
569 output.push_str(&self.alphas.to_vmf_string(indent_level + 1, "alphas"));
570 output.push_str(
571 &self
572 .triangle_tags
573 .to_vmf_string(indent_level + 1, "triangle_tags"),
574 );
575 output.push_str(&Self::allowed_verts_to_vmf_string(
576 &self.allowed_verts,
577 indent_level + 1,
578 ));
579 output.push_str(&format!("{}}}\n", indent));
580
581 output
582 }
583}
584
585impl DispInfo {
586 fn parse_allowed_verts(block: VmfBlock) -> VmfResult<IndexMap<String, Vec<i32>>> {
596 let mut allowed_verts = IndexMap::new();
597 for (key, value) in block.key_values.into_iter() {
598 let verts: VmfResult<Vec<i32>> = value
599 .split_whitespace()
600 .map(|s| {
601 s.parse::<i32>().map_err(|e| VmfError::ParseInt {
602 source: e,
603 key: s.to_string(),
604 })
605 })
606 .collect();
607 allowed_verts.insert(key, verts?);
608 }
609 Ok(allowed_verts)
610 }
611
612 fn allowed_verts_into_vmf_block(allowed_verts: IndexMap<String, Vec<i32>>) -> VmfBlock {
622 let mut key_values = IndexMap::new();
623 for (key, values) in allowed_verts {
624 key_values.insert(
625 key,
626 values
627 .iter()
628 .map(|v| v.to_string())
629 .collect::<Vec<String>>()
630 .join(" "),
631 );
632 }
633
634 VmfBlock {
635 name: "allowed_verts".to_string(),
636 key_values,
637 blocks: Vec::new(),
638 }
639 }
640
641 fn allowed_verts_to_vmf_string(
652 allowed_verts: &IndexMap<String, Vec<i32>>,
653 indent_level: usize,
654 ) -> String {
655 let indent = "\t".repeat(indent_level);
656 let mut output = String::new();
657
658 output.push_str(&format!("{}allowed_verts\n", indent));
659 output.push_str(&format!("{}{{\n", indent));
660 for (key, values) in allowed_verts {
661 output.push_str(&format!(
662 "{}\t\"{}\" \"{}\"\n",
663 indent,
664 key,
665 values
666 .iter()
667 .map(|v| v.to_string())
668 .collect::<Vec<String>>()
669 .join(" ")
670 ));
671 }
672 output.push_str(&format!("{}}}\n", indent));
673
674 output
675 }
676}
677
678#[derive(Debug, Default, Clone, PartialEq, Eq)]
680#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
681pub struct DispRows {
682 pub rows: Vec<String>,
684}
685
686impl TryFrom<VmfBlock> for DispRows {
687 type Error = VmfError;
688
689 fn try_from(block: VmfBlock) -> VmfResult<Self> {
690 let mut rows = Vec::with_capacity(block.key_values.len());
691 for (key, value) in block.key_values {
692 if let Some(stripped_idx) = key.strip_prefix("row") {
693 let index = stripped_idx
694 .parse::<usize>()
695 .map_err(|e| VmfError::ParseInt {
696 source: e,
697 key: key.to_string(),
698 })?;
699 if index >= rows.len() {
700 rows.resize(index + 1, String::new());
701 }
702 rows[index] = value;
703 }
704 }
705 Ok(DispRows { rows })
706 }
707}
708
709impl DispRows {
710 fn into_vmf_block(self, name: &str) -> VmfBlock {
721 let mut key_values = IndexMap::new();
722 for (i, row) in self.rows.into_iter().enumerate() {
723 key_values.insert(format!("row{}", i), row);
724 }
725
726 VmfBlock {
727 name: name.to_string(),
728 key_values,
729 blocks: Vec::new(),
730 }
731 }
732
733 fn to_vmf_string(&self, indent_level: usize, name: &str) -> String {
745 let indent = "\t".repeat(indent_level);
746 let mut output = String::with_capacity(32);
747
748 output.push_str(&format!("{}{}\n", indent, name));
749 output.push_str(&format!("{}{{\n", indent));
750 for (i, row) in self.rows.iter().enumerate() {
751 output.push_str(&format!("{}\t\"row{}\" \"{}\"\n", indent, i, row));
752 }
753 output.push_str(&format!("{}}}\n", indent));
754
755 output
756 }
757}
758
759#[derive(Debug, Default, Clone, PartialEq, Eq)]
761#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
762pub struct Group {
763 pub id: u32,
765 pub editor: Editor,
767}
768
769impl TryFrom<VmfBlock> for Group {
770 type Error = VmfError;
771
772 fn try_from(mut block: VmfBlock) -> VmfResult<Self> {
773 let mut editor = None;
774 for inner_block in block.blocks {
775 if inner_block.name.eq_ignore_ascii_case("editor") {
776 editor = Some(Editor::try_from(inner_block)?);
777 }
778 }
779
780 Ok(Self {
781 id: take_and_parse_key::<u32>(&mut block.key_values, "id")?,
782 editor: editor.unwrap_or_default(),
783 })
784 }
785}
786
787impl From<Group> for VmfBlock {
788 fn from(val: Group) -> Self {
789 let mut blocks = Vec::with_capacity(2);
790
791 blocks.push(val.editor.into());
793
794 VmfBlock {
795 name: "group".to_string(),
796 key_values: {
797 let mut key_values = IndexMap::new();
798 key_values.insert("id".to_string(), val.id.to_string());
799 key_values
800 },
801 blocks,
802 }
803 }
804}
805
806impl VmfSerializable for Group {
807 fn to_vmf_string(&self, indent_level: usize) -> String {
808 let indent = "\t".repeat(indent_level);
809 let mut output = String::with_capacity(64);
810
811 output.push_str(&format!("{0}group\n{0}{{\n", indent));
813 output.push_str(&format!("{}\t\"id\" \"{}\"\n", indent, self.id));
814
815 output.push_str(&self.editor.to_vmf_string(indent_level + 1));
817
818 output.push_str(&format!("{}}}\n", indent));
819
820 output
821 }
822}