1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use crate::demo::sendprop::{read_bit_coord, write_bit_coord};
use crate::demo::vector::Vector;
use crate::{ReadResult, Stream};
use bitbuffer::{BitRead, BitWrite, BitWriteSized, BitWriteStream, LittleEndian};
use serde::{Deserialize, Serialize};

#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BSPDecalMessage {
    pub position: Vector,
    pub texture_index: u16,
    pub ent_index: u16,
    pub model_index: u16,
    pub low_priority: bool,
}

impl BitRead<'_, LittleEndian> for BSPDecalMessage {
    fn read(stream: &mut Stream) -> ReadResult<Self> {
        let position = {
            let (has_x, has_y, has_z) = stream.read()?;

            Vector {
                x: if has_x { read_bit_coord(stream)? } else { 0f32 },
                y: if has_y { read_bit_coord(stream)? } else { 0f32 },
                z: if has_z { read_bit_coord(stream)? } else { 0f32 },
            }
        };

        let texture_index = stream.read_sized(9)?;
        let (ent_index, model_index): (u16, u16) = if stream.read()? {
            (stream.read_sized(11)?, stream.read_sized(13)?)
        } else {
            (0, 0)
        };
        let low_priority = stream.read()?;

        Ok(BSPDecalMessage {
            position,
            texture_index,
            ent_index,
            model_index,
            low_priority,
        })
    }
}

impl BitWrite<LittleEndian> for BSPDecalMessage {
    fn write(&self, stream: &mut BitWriteStream<LittleEndian>) -> ReadResult<()> {
        let has_x = self.position.x != 0.0;
        let has_y = self.position.y != 0.0;
        let has_z = self.position.z != 0.0;
        (has_x, has_y, has_z).write(stream)?;

        if has_x {
            write_bit_coord(self.position.x, stream)?;
        }
        if has_y {
            write_bit_coord(self.position.y, stream)?;
        }
        if has_z {
            write_bit_coord(self.position.z, stream)?;
        }
        self.texture_index.write_sized(stream, 9)?;
        if self.ent_index != 0 || self.model_index != 0 {
            true.write(stream)?;
            self.ent_index.write_sized(stream, 11)?;
            self.model_index.write_sized(stream, 13)?;
        } else {
            false.write(stream)?;
        }
        self.low_priority.write(stream)?;

        Ok(())
    }
}

#[test]
fn test_decal_roundtrip() {
    crate::test_roundtrip_write(BSPDecalMessage {
        position: Vector::default(),
        texture_index: 0,
        ent_index: 0,
        model_index: 0,
        low_priority: false,
    });
    crate::test_roundtrip_write(BSPDecalMessage {
        position: Vector {
            x: 1.0,
            y: 0.5,
            z: 0.0,
        },
        texture_index: 12,
        ent_index: 15,
        model_index: 2,
        low_priority: true,
    });
}