1use indexmap::IndexMap;
4use serde::{Deserialize, Serialize};
5
6use super::common::Editor;
7use crate::utils::{get_key, parse_hs_key, To01String};
8use crate::{
9 errors::{VmfError, VmfResult},
10 VmfBlock, VmfSerializable,
11};
12
13#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
15pub struct World {
16 pub key_values: IndexMap<String, String>,
18 pub solids: Vec<Solid>,
20 pub hidden: Vec<Solid>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub group: Option<Group>,
25}
26
27impl TryFrom<VmfBlock> for World {
28 type Error = VmfError;
29
30 fn try_from(block: VmfBlock) -> VmfResult<Self> {
31 let mut world = World {
32 key_values: block.key_values,
33 ..Default::default()
34 };
35
36 for inner_block in block.blocks {
37 match inner_block.name.as_str() {
38 "solid" => world.solids.push(Solid::try_from(inner_block)?),
39 "group" => world.group = Group::try_from(inner_block).ok(),
40 "hidden" => {
41 if let Some(hidden_block) = inner_block.blocks.first() {
42 world.hidden.push(Solid::try_from(hidden_block.to_owned())?);
43 }
44 }
45 _ => {
46 #[cfg(feature = "debug_assert_info")]
48 debug_assert!(false, "Unexpected block name: {}", inner_block.name);
49 }
50 };
51 }
52
53 Ok(world)
54 }
55}
56
57impl From<World> for VmfBlock {
58 fn from(val: World) -> Self {
59 let mut blocks = Vec::new();
60
61 for solid in val.solids {
63 blocks.push(solid.into());
64 }
65
66 for hidden_solid in val.hidden {
68 blocks.push(VmfBlock {
69 name: "hidden".to_string(),
70 key_values: IndexMap::new(),
71 blocks: vec![hidden_solid.into()],
72 });
73 }
74
75 if let Some(group) = val.group {
77 blocks.push(group.into());
78 }
79
80 VmfBlock {
81 name: "world".to_string(),
82 key_values: val.key_values,
83 blocks,
84 }
85 }
86}
87
88impl VmfSerializable for World {
89 fn to_vmf_string(&self, indent_level: usize) -> String {
90 let indent = "\t".repeat(indent_level);
91 let mut output = String::with_capacity(2048);
92
93 output.push_str(&format!("{0}world\n{0}{{\n", indent));
94
95 for (key, value) in &self.key_values {
97 output.push_str(&format!("{}\t\"{}\" \"{}\"\n", indent, key, value));
98 }
99
100 if !self.solids.is_empty() {
102 for solid in &self.solids {
103 output.push_str(&solid.to_vmf_string(indent_level + 1));
104 }
105 }
106
107 if !self.hidden.is_empty() {
109 output.push_str(&format!("{0}\tHidden\n{0}\t{{\n", indent));
110 for solid in &self.hidden {
111 output.push_str(&solid.to_vmf_string(indent_level + 2));
112 }
113 output.push_str(&format!("{}\t}}\n", indent));
114 }
115
116 if let Some(group) = &self.group {
118 output.push_str(&group.to_vmf_string(indent_level + 1));
119 }
120
121 output.push_str(&format!("{}}}\n", indent));
122 output
123 }
124}
125
126#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
128pub struct Solid {
129 pub id: u64,
131 pub sides: Vec<Side>,
133 pub editor: Editor,
135}
136
137impl TryFrom<VmfBlock> for Solid {
138 type Error = VmfError;
139
140 fn try_from(block: VmfBlock) -> VmfResult<Self> {
141 let mut solid = Solid {
142 id: parse_hs_key!(&block.key_values, "id", u64)?,
143 sides: Vec::with_capacity(4),
144 ..Default::default()
145 };
146
147 for inner_block in block.blocks {
148 match inner_block.name.as_str() {
149 "side" => solid.sides.push(Side::try_from(inner_block)?),
150 "editor" => solid.editor = Editor::try_from(inner_block)?,
151 _ => {
152 #[cfg(feature = "debug_assert_info")]
153 debug_assert!(false, "Unexpected block name: {}", inner_block.name);
154 }
155 }
156 }
157
158 Ok(solid)
159 }
160}
161
162impl From<Solid> for VmfBlock {
163 fn from(val: Solid) -> Self {
164 let mut blocks = Vec::new();
165
166 for side in val.sides {
168 blocks.push(side.into());
169 }
170
171 blocks.push(val.editor.into());
173
174 VmfBlock {
175 name: "solid".to_string(),
176 key_values: {
177 let mut key_values = IndexMap::new();
178 key_values.insert("id".to_string(), val.id.to_string());
179 key_values
180 },
181 blocks,
182 }
183 }
184}
185
186impl VmfSerializable for Solid {
187 fn to_vmf_string(&self, indent_level: usize) -> String {
188 let indent = "\t".repeat(indent_level);
189 let mut output = String::with_capacity(256);
190
191 output.push_str(&format!("{0}solid\n{0}{{\n", indent));
193 output.push_str(&format!("{}\t\"id\" \"{}\"\n", indent, self.id));
194
195 for side in &self.sides {
197 output.push_str(&side.to_vmf_string(indent_level + 1));
198 }
199
200 output.push_str(&self.editor.to_vmf_string(indent_level + 1));
202
203 output.push_str(&format!("{}}}\n", indent));
204
205 output
206 }
207}
208
209#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
211pub struct Side {
212 pub id: u32,
214 pub plane: String,
216 pub material: String,
218 pub u_axis: String,
220 pub v_axis: String,
222 #[serde(default, skip_serializing_if = "Option::is_none")]
224 pub rotation: Option<f32>,
225 pub lightmap_scale: u16,
227 pub smoothing_groups: i32,
229 #[serde(default, skip_serializing_if = "Option::is_none")]
231 pub flags: Option<u32>,
232 #[serde(default, skip_serializing_if = "Option::is_none")]
234 pub dispinfo: Option<DispInfo>,
235}
236
237impl TryFrom<VmfBlock> for Side {
238 type Error = VmfError;
239
240 fn try_from(block: VmfBlock) -> VmfResult<Self> {
241 let kv = &block.key_values;
242 let dispinfo_block = block.blocks.iter().find(|b| b.name == "dispinfo");
243
244 Ok(Side {
245 id: parse_hs_key!(kv, "id", u32)?,
246 plane: get_key!(kv, "plane")?.to_owned(),
247 material: get_key!(kv, "material")?.to_owned(),
248 u_axis: get_key!(kv, "uaxis")?.to_owned(),
249 v_axis: get_key!(kv, "vaxis")?.to_owned(),
250 rotation: get_key!(kv, "rotation", "_".into()).parse().ok(),
251 lightmap_scale: parse_hs_key!(kv, "lightmapscale", u16)?,
252 smoothing_groups: parse_hs_key!(kv, "smoothing_groups", i32)?,
253 flags: get_key!(kv, "flags", "_".into()).parse().ok(),
254 dispinfo: match dispinfo_block {
255 Some(block) => Some(DispInfo::try_from(block.clone())?), None => None,
257 },
258 })
259 }
260}
261
262impl From<Side> for VmfBlock {
263 fn from(val: Side) -> Self {
264 let mut key_values = IndexMap::new();
265 key_values.insert("id".to_string(), val.id.to_string());
266 key_values.insert("plane".to_string(), val.plane);
267 key_values.insert("material".to_string(), val.material);
268 key_values.insert("uaxis".to_string(), val.u_axis);
269 key_values.insert("vaxis".to_string(), val.v_axis);
270 if let Some(rotation) = val.rotation {
271 key_values.insert("rotation".to_string(), rotation.to_string());
272 }
273 key_values.insert("lightmapscale".to_string(), val.lightmap_scale.to_string());
274 key_values.insert(
275 "smoothing_groups".to_string(),
276 val.smoothing_groups.to_string(),
277 );
278 if let Some(flags) = val.flags {
279 key_values.insert("flags".to_string(), flags.to_string());
280 }
281
282 VmfBlock {
283 name: "side".to_string(),
284 key_values,
285 blocks: Vec::new(),
286 }
287 }
288}
289
290impl VmfSerializable for Side {
291 fn to_vmf_string(&self, indent_level: usize) -> String {
292 let indent = "\t".repeat(indent_level);
293 let mut output = String::with_capacity(256);
294
295 output.push_str(&format!("{0}side\n{0}{{\n", indent));
297
298 output.push_str(&format!("{}\t\"id\" \"{}\"\n", indent, self.id));
300 output.push_str(&format!("{}\t\"plane\" \"{}\"\n", indent, self.plane));
301 output.push_str(&format!("{}\t\"material\" \"{}\"\n", indent, self.material));
302 output.push_str(&format!("{}\t\"uaxis\" \"{}\"\n", indent, self.u_axis));
303 output.push_str(&format!("{}\t\"vaxis\" \"{}\"\n", indent, self.v_axis));
304
305 if let Some(rotation) = self.rotation {
306 output.push_str(&format!("{}\t\"rotation\" \"{}\"\n", indent, rotation));
307 }
308
309 output.push_str(&format!(
310 "{}\t\"lightmapscale\" \"{}\"\n",
311 indent, self.lightmap_scale
312 ));
313 output.push_str(&format!(
314 "{}\t\"smoothing_groups\" \"{}\"\n",
315 indent, self.smoothing_groups
316 ));
317
318 if let Some(flags) = self.flags {
320 output.push_str(&format!("{}\t\"flags\" \"{}\"\n", indent, flags));
321 }
322
323 if let Some(dispinfo) = &self.dispinfo {
324 output.push_str(&dispinfo.to_vmf_string(indent_level + 1));
325 }
326
327 output.push_str(&format!("{0}}}\n", indent));
329
330 output
331 }
332}
333
334macro_rules! find_block {
345 ($blocks:expr, $name:expr) => {
346 $blocks.iter().find(|b| b.name == $name).ok_or_else(|| {
347 VmfError::InvalidFormat(format!("Missing {} block in dispinfo", $name))
348 })?
349 };
350}
351
352#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
354pub struct DispInfo {
355 pub power: u8,
357 pub start_position: String,
359 #[serde(default, skip_serializing_if = "Option::is_none")]
361 pub flags: Option<u32>,
362 pub elevation: f32,
364 pub subdiv: bool,
366 pub normals: DispRows,
368 pub distances: DispRows,
370 pub offsets: DispRows,
372 pub offset_normals: DispRows,
374 pub alphas: DispRows,
376 pub triangle_tags: DispRows,
378 pub allowed_verts: IndexMap<String, Vec<i32>>,
380}
381
382impl TryFrom<VmfBlock> for DispInfo {
383 type Error = VmfError;
384
385 fn try_from(block: VmfBlock) -> VmfResult<Self> {
386 let normals_block = find_block!(block.blocks, "normals");
387 let distances_block = find_block!(block.blocks, "distances");
388 let alphas_block = find_block!(block.blocks, "alphas");
389 let triangle_tags_block = find_block!(block.blocks, "triangle_tags");
390 let allowed_verts_block = find_block!(block.blocks, "allowed_verts");
391
392 let offsets = block.blocks.iter()
394 .find(|b| b.name == "offsets")
395 .map_or_else(
396 || Ok(DispRows::default()),
397 |b| DispRows::try_from(b.clone())
398 )?;
399
400 let offset_normals = block.blocks.iter()
401 .find(|b| b.name == "offset_normals")
402 .map_or_else(
403 || Ok(DispRows::default()),
404 |b| DispRows::try_from(b.clone())
405 )?;
406
407
408 let kv = &block.key_values;
409 Ok(DispInfo {
410 power: parse_hs_key!(kv, "power", u8)?,
411 start_position: get_key!(kv, "startposition")?.to_string(),
412 flags: get_key!(kv, "flags", "_".into()).parse().ok(),
413 elevation: get_key!(kv, "elevation")?.parse()?,
414 subdiv: get_key!(kv, "subdiv")? == "1",
415 normals: DispRows::try_from(normals_block.clone())?,
416 distances: DispRows::try_from(distances_block.clone())?,
417 offsets,
418 offset_normals,
419 alphas: DispRows::try_from(alphas_block.clone())?,
420 triangle_tags: DispRows::try_from(triangle_tags_block.clone())?,
421 allowed_verts: DispInfo::parse_allowed_verts(allowed_verts_block)?,
422 })
423 }
424}
425
426impl From<DispInfo> for VmfBlock {
427 fn from(val: DispInfo) -> Self {
428 let blocks = vec![
429 val.normals.into_vmf_block("normals"),
430 val.distances.into_vmf_block("distances"),
431 val.offsets.into_vmf_block("offsets"),
432 val.offset_normals.into_vmf_block("offset_normals"),
433 val.alphas.into_vmf_block("alphas"),
434 val.triangle_tags.into_vmf_block("triangle_tags"),
435 DispInfo::allowed_verts_into_vmf_block(val.allowed_verts),
436 ];
437
438 let mut key_values = IndexMap::new();
439 key_values.insert("power".to_string(), val.power.to_string());
440 key_values.insert("startposition".to_string(), val.start_position);
441 key_values.insert("elevation".to_string(), val.elevation.to_string());
442 key_values.insert("subdiv".to_string(), val.subdiv.to_01_string());
443
444 if let Some(flags) = val.flags {
445 key_values.insert("flags".to_string(), flags.to_string());
446 }
447
448 VmfBlock {
449 name: "dispinfo".to_string(),
450 key_values,
451 blocks,
452 }
453 }
454}
455
456impl VmfSerializable for DispInfo {
457 fn to_vmf_string(&self, indent_level: usize) -> String {
458 let indent = "\t".repeat(indent_level);
459 let mut output = String::with_capacity(256);
460
461 output.push_str(&format!("{}dispinfo\n", indent));
462 output.push_str(&format!("{}{{\n", indent));
463 output.push_str(&format!("{}\t\"power\" \"{}\"\n", indent, self.power));
464 output.push_str(&format!(
465 "{}\t\"startposition\" \"{}\"\n",
466 indent, self.start_position
467 ));
468
469 if let Some(flags) = self.flags {
471 output.push_str(&format!("{}\t\"flags\" \"{}\"\n", indent, flags));
472 }
473
474 output.push_str(&format!(
475 "{}\t\"elevation\" \"{}\"\n",
476 indent, self.elevation
477 ));
478 output.push_str(&format!(
479 "{}\t\"subdiv\" \"{}\"\n",
480 indent,
481 self.subdiv.to_01_string()
482 ));
483 output.push_str(&self.normals.to_vmf_string(indent_level + 1, "normals"));
484 output.push_str(&self.distances.to_vmf_string(indent_level + 1, "distances"));
485 output.push_str(&self.offsets.to_vmf_string(indent_level + 1, "offsets"));
486 output.push_str(
487 &self
488 .offset_normals
489 .to_vmf_string(indent_level + 1, "offset_normals"),
490 );
491 output.push_str(&self.alphas.to_vmf_string(indent_level + 1, "alphas"));
492 output.push_str(
493 &self
494 .triangle_tags
495 .to_vmf_string(indent_level + 1, "triangle_tags"),
496 );
497 output.push_str(&Self::allowed_verts_to_vmf_string(
498 &self.allowed_verts,
499 indent_level + 1,
500 ));
501 output.push_str(&format!("{}}}\n", indent));
502
503 output
504 }
505}
506
507impl DispInfo {
508 fn parse_allowed_verts(block: &VmfBlock) -> VmfResult<IndexMap<String, Vec<i32>>> {
518 let mut allowed_verts = IndexMap::new();
519 for (key, value) in &block.key_values {
520 let verts: VmfResult<Vec<i32>> = value
521 .split_whitespace()
522 .map(|s| {
523 s.parse::<i32>()
524 .map_err(|e| VmfError::ParseInt(e, s.to_string()))
525 })
526 .collect();
527 allowed_verts.insert(key.clone(), verts?);
528 }
529 Ok(allowed_verts)
530 }
531
532 fn allowed_verts_into_vmf_block(allowed_verts: IndexMap<String, Vec<i32>>) -> VmfBlock {
542 let mut key_values = IndexMap::new();
543 for (key, values) in allowed_verts {
544 key_values.insert(
545 key,
546 values
547 .iter()
548 .map(|v| v.to_string())
549 .collect::<Vec<String>>()
550 .join(" "),
551 );
552 }
553
554 VmfBlock {
555 name: "allowed_verts".to_string(),
556 key_values,
557 blocks: Vec::new(),
558 }
559 }
560
561 fn allowed_verts_to_vmf_string(
572 allowed_verts: &IndexMap<String, Vec<i32>>,
573 indent_level: usize,
574 ) -> String {
575 let indent = "\t".repeat(indent_level);
576 let mut output = String::new();
577
578 output.push_str(&format!("{}allowed_verts\n", indent));
579 output.push_str(&format!("{}{{\n", indent));
580 for (key, values) in allowed_verts {
581 output.push_str(&format!(
582 "{}\t\"{}\" \"{}\"\n",
583 indent,
584 key,
585 values
586 .iter()
587 .map(|v| v.to_string())
588 .collect::<Vec<String>>()
589 .join(" ")
590 ));
591 }
592 output.push_str(&format!("{}}}\n", indent));
593
594 output
595 }
596}
597
598#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
600pub struct DispRows {
601 pub rows: Vec<String>,
603}
604
605impl TryFrom<VmfBlock> for DispRows {
606 type Error = VmfError;
607
608 fn try_from(block: VmfBlock) -> VmfResult<Self> {
609 let mut rows = Vec::with_capacity(block.key_values.len());
610 for (key, value) in block.key_values {
611 if let Some(stripped_idx) = key.strip_prefix("row") {
612 let index = stripped_idx
613 .parse::<usize>()
614 .map_err(|e| VmfError::ParseInt(e, key.to_string()))?;
615 if index >= rows.len() {
616 rows.resize(index + 1, String::new());
617 }
618 rows[index] = value;
619 }
620 }
621 Ok(DispRows { rows })
622 }
623}
624
625impl DispRows {
626 fn into_vmf_block(self, name: &str) -> VmfBlock {
637 let mut key_values = IndexMap::new();
638 for (i, row) in self.rows.into_iter().enumerate() {
639 key_values.insert(format!("row{}", i), row);
640 }
641
642 VmfBlock {
643 name: name.to_string(),
644 key_values,
645 blocks: Vec::new(),
646 }
647 }
648
649 fn to_vmf_string(&self, indent_level: usize, name: &str) -> String {
661 let indent = "\t".repeat(indent_level);
662 let mut output = String::with_capacity(32);
663
664 output.push_str(&format!("{}{}\n", indent, name));
665 output.push_str(&format!("{}{{\n", indent));
666 for (i, row) in self.rows.iter().enumerate() {
667 output.push_str(&format!("{}\t\"row{}\" \"{}\"\n", indent, i, row));
668 }
669 output.push_str(&format!("{}}}\n", indent));
670
671 output
672 }
673}
674
675#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
677pub struct Group {
678 pub id: u32,
680 pub editor: Editor,
682}
683
684impl TryFrom<VmfBlock> for Group {
685 type Error = VmfError;
686
687 fn try_from(block: VmfBlock) -> VmfResult<Self> {
688 let mut editor = None;
689 for inner_block in block.blocks {
690 if inner_block.name.eq_ignore_ascii_case("editor") {
691 editor = Some(Editor::try_from(inner_block)?);
692 }
693 }
694
695 Ok(Self {
696 id: parse_hs_key!(&block.key_values, "id", u32)?,
697 editor: editor.unwrap_or_default(),
698 })
699 }
700}
701
702impl From<Group> for VmfBlock {
703 fn from(val: Group) -> Self {
704 let mut blocks = Vec::with_capacity(2);
705
706 blocks.push(val.editor.into());
708
709 VmfBlock {
710 name: "group".to_string(),
711 key_values: {
712 let mut key_values = IndexMap::new();
713 key_values.insert("id".to_string(), val.id.to_string());
714 key_values
715 },
716 blocks,
717 }
718 }
719}
720
721impl VmfSerializable for Group {
722 fn to_vmf_string(&self, indent_level: usize) -> String {
723 let indent = "\t".repeat(indent_level);
724 let mut output = String::with_capacity(64);
725
726 output.push_str(&format!("{0}group\n{0}{{\n", indent));
728 output.push_str(&format!("{}\t\"id\" \"{}\"\n", indent, self.id));
729
730 output.push_str(&self.editor.to_vmf_string(indent_level + 1));
732
733 output.push_str(&format!("{}}}\n", indent));
734
735 output
736 }
737}