libtw2_demo/ddnet/
reader.rs

1use libtw2_gamenet_common::snap_obj::TypeId;
2use libtw2_gamenet_common::traits;
3use libtw2_gamenet_common::traits::MessageExt as _;
4use libtw2_gamenet_common::traits::Protocol;
5use libtw2_gamenet_common::traits::ProtocolStatic;
6use libtw2_packer::IntUnpacker;
7use libtw2_packer::Unpacker;
8use libtw2_snapshot::snap;
9use libtw2_snapshot::Delta;
10use libtw2_snapshot::Snap;
11use libtw2_snapshot::SnapReader;
12use std::collections::HashMap;
13use std::io;
14use std::marker::PhantomData;
15use std::mem;
16use std::slice;
17use thiserror::Error;
18use uuid::Uuid;
19use warn::wrap;
20use warn::Warn;
21
22use crate::format;
23use crate::reader;
24use crate::RawChunk;
25
26#[derive(Error, Debug)]
27pub enum ReadError {
28    #[error(transparent)]
29    Inner(#[from] reader::ReadError),
30    #[error("Snap parsing - {0:?}")]
31    Snap(snap::Error),
32    #[error("Uuid item has an incorrect size")]
33    UuidItemLength,
34    #[error("Uuid type id that is not registered")]
35    UnregisteredUuidTypeId,
36    #[error("Chunk types are not in the proper order")]
37    ChunkOrder,
38}
39
40pub struct DemoReader<P: for<'a> Protocol<'a>> {
41    raw: reader::Reader,
42    delta: Delta,
43    snap: Snap,
44    old_snap: Snap,
45    snap_reader: SnapReader,
46    snapshot: Snapshot<P::SnapObj>,
47    protocol: PhantomData<P>,
48}
49
50#[derive(Debug)]
51pub enum Warning {
52    Demo(format::Warning),
53    Snapshot(libtw2_snapshot::format::Warning),
54    Packer(libtw2_packer::Warning),
55    ExcessItemData,
56    Gamenet(libtw2_gamenet_common::error::Error),
57}
58
59impl From<format::Warning> for Warning {
60    fn from(w: format::Warning) -> Self {
61        Warning::Demo(w)
62    }
63}
64impl From<libtw2_snapshot::format::Warning> for Warning {
65    fn from(w: libtw2_snapshot::format::Warning) -> Self {
66        Warning::Snapshot(w)
67    }
68}
69impl From<libtw2_packer::Warning> for Warning {
70    fn from(w: libtw2_packer::Warning) -> Self {
71        Warning::Packer(w)
72    }
73}
74impl From<libtw2_packer::ExcessData> for Warning {
75    fn from(_: libtw2_packer::ExcessData) -> Self {
76        Warning::ExcessItemData
77    }
78}
79
80pub enum Chunk<'a, P: Protocol<'a>> {
81    Message(P::Game),
82    Snapshot(slice::Iter<'a, (P::SnapObj, u16)>),
83    Tick(i32),
84    Invalid,
85}
86
87impl<P: for<'a> Protocol<'a>> DemoReader<P> {
88    pub fn new<R, W>(data: R, warn: &mut W) -> Result<Self, ReadError>
89    where
90        R: io::Read + io::Seek + 'static,
91        W: Warn<Warning>,
92    {
93        let reader = reader::Reader::new(data, wrap(warn))?;
94        Ok(DemoReader {
95            raw: reader,
96            delta: Delta::new(),
97            snap: Snap::empty(),
98            old_snap: Snap::empty(),
99            snap_reader: SnapReader::new(),
100            snapshot: Snapshot::default(),
101            protocol: PhantomData,
102        })
103    }
104
105    pub fn next_chunk<W: Warn<Warning>>(
106        &mut self,
107        warn: &mut W,
108    ) -> Result<Option<Chunk<P>>, ReadError> {
109        match self.raw.read_chunk(wrap(warn))? {
110            None => return Ok(None),
111            Some(RawChunk::Unknown) => Ok(Some(Chunk::Invalid)),
112            Some(RawChunk::Tick { tick, .. }) => Ok(Some(Chunk::Tick(tick))),
113            Some(RawChunk::Message(msg)) => {
114                let mut unpacker = Unpacker::new_from_demo(msg);
115                match P::Game::decode(wrap(warn), &mut unpacker) {
116                    Ok(msg) => Ok(Some(Chunk::Message(msg))),
117                    Err(err) => {
118                        warn.warn(Warning::Gamenet(err));
119                        Ok(Some(Chunk::Invalid))
120                    }
121                }
122            }
123            Some(RawChunk::Snapshot(snap)) => {
124                let mut unpacker = Unpacker::new(snap);
125                let mut swap = Snap::empty();
126                mem::swap(&mut self.snap, &mut swap);
127                self.snap = self
128                    .snap_reader
129                    .read(wrap(warn), swap, &mut unpacker)
130                    .unwrap();
131                self.snapshot.build::<P, _>(warn, &self.snap)?;
132                Ok(Some(Chunk::Snapshot(self.snapshot.objects.iter())))
133            }
134            Some(RawChunk::SnapshotDelta(dt)) => {
135                let mut unpacker = Unpacker::new(dt);
136                self.delta
137                    .read(wrap(warn), P::obj_size, &mut unpacker)
138                    .map_err(ReadError::Snap)?;
139                self.old_snap
140                    .read_with_delta(wrap(warn), &self.snap, &self.delta)
141                    .map_err(ReadError::Snap)?;
142                mem::swap(&mut self.old_snap, &mut self.snap);
143                self.snapshot.build::<P, _>(warn, &self.snap)?;
144                Ok(Some(Chunk::Snapshot(self.snapshot.objects.iter())))
145            }
146        }
147    }
148
149    pub fn inner(&self) -> &reader::Reader {
150        &self.raw
151    }
152}
153
154struct Snapshot<T> {
155    uuid_index: HashMap<u16, Uuid>,
156    pub objects: Vec<(T, u16)>,
157}
158
159impl<T> Default for Snapshot<T> {
160    fn default() -> Snapshot<T> {
161        Snapshot {
162            uuid_index: Default::default(),
163            objects: Default::default(),
164        }
165    }
166}
167
168impl<T> Snapshot<T> {
169    fn build<P, W>(&mut self, warn: &mut W, snap: &Snap) -> Result<(), ReadError>
170    where
171        P: ProtocolStatic<SnapObj = T>,
172        T: traits::SnapObj,
173        W: Warn<Warning>,
174    {
175        self.uuid_index.clear();
176        self.objects.clear();
177
178        // First we build the uuid item index
179        for item in snap.items().filter(|item| item.type_id == 0) {
180            let mut uuid_bytes = [0; 16];
181            if item.data.len() != 4 {
182                return Err(ReadError::UuidItemLength);
183            }
184            for (b, x) in uuid_bytes.chunks_mut(4).zip(item.data) {
185                b.copy_from_slice(&x.to_be_bytes());
186            }
187            let uuid = Uuid::from_bytes(uuid_bytes);
188            self.uuid_index.insert(item.id, uuid);
189        }
190
191        for item in snap.items().filter(|item| item.type_id != 0) {
192            let type_id = if item.type_id < u16::MAX / 4 {
193                TypeId::Ordinal(item.type_id)
194            } else {
195                let uuid = self
196                    .uuid_index
197                    .get(&item.type_id)
198                    .ok_or(ReadError::UnregisteredUuidTypeId)?;
199                TypeId::Uuid(*uuid)
200            };
201            let mut int_unpacker = IntUnpacker::new(item.data);
202            match P::SnapObj::decode_obj(wrap(warn), type_id, &mut int_unpacker) {
203                Ok(obj) => self.objects.push((obj, item.id)),
204                Err(err) => warn.warn(Warning::Gamenet(err)),
205            }
206        }
207
208        Ok(())
209    }
210}