mindus/data/
mod.rs

1//! all the IO
2use flate2::{
3    Compress, CompressError as CError, Compression, Decompress, DecompressError as DError,
4    FlushCompress, FlushDecompress, Status,
5};
6use std::backtrace::Backtrace;
7use std::collections::HashMap;
8use std::error::Error;
9use std::fmt;
10use std::str::Utf8Error;
11use thiserror::Error;
12
13pub(crate) mod autotile;
14mod base64;
15pub mod command;
16pub mod dynamic;
17pub mod entity_mapping;
18pub mod map;
19pub mod planet;
20pub mod renderer;
21pub mod schematic;
22pub mod sector;
23pub mod weather;
24
25#[derive(Debug)]
26pub struct DataRead<'d> {
27    pub(crate) data: &'d [u8],
28    // used with read_chunk
29    read: usize,
30}
31
32impl fmt::Display for DataRead<'_> {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        write!(f, "{}", String::from_utf8_lossy(self.data))
35    }
36}
37
38macro_rules! make_read {
39    ($name:ident, $type:ty) => {
40        pub fn $name(&mut self) -> Result<$type, ReadError> {
41            const LEN: usize = std::mem::size_of::<$type>();
42            let output = self.readN::<LEN>()?;
43            Ok(<$type>::from_be_bytes(output))
44        }
45    };
46}
47
48impl<'d> DataRead<'d> {
49    #[must_use]
50    pub const fn new(data: &'d [u8]) -> Self {
51        Self { data, read: 0 }
52    }
53
54    pub fn read_bool(&mut self) -> Result<bool, ReadError> {
55        Ok(self.read_u8()? != 0)
56    }
57
58    make_read!(read_u8, u8);
59    make_read!(read_i8, i8);
60    make_read!(read_u16, u16);
61    make_read!(read_i16, i16);
62    make_read!(read_u32, u32);
63    make_read!(read_i32, i32);
64    make_read!(read_f32, f32);
65    make_read!(read_u64, u64);
66    make_read!(read_i64, i64);
67    make_read!(read_f64, f64);
68
69    pub fn read_utf(&mut self) -> Result<&'d str, ReadError> {
70        let len = self.read_u16()?;
71        let result = std::str::from_utf8(self.eat(len as usize)?)?;
72        Ok(result)
73    }
74
75    pub fn eat(&mut self, n: usize) -> Result<&'d [u8], ReadError> {
76        self.data
77            .split_off(..n)
78            .ok_or(ReadError::Underflow {
79                need: n,
80                have: self.data.len(),
81                bt: Backtrace::capture(),
82            })
83            .inspect(|_| self.read += n)
84    }
85
86    #[allow(non_snake_case)]
87    pub fn readN<const N: usize>(&mut self) -> Result<[u8; N], ReadError> {
88        self.eat(N).map(|x| x.try_into().unwrap())
89    }
90
91    pub fn read_bytes(&mut self, dst: &mut [u8]) -> Result<(), ReadError> {
92        dst.copy_from_slice(self.eat(dst.len())?);
93        Ok(())
94    }
95
96    pub fn skip(&mut self, n: usize) -> Result<(), ReadError> {
97        self.data = self.data.get(n..).ok_or(ReadError::Underflow {
98            need: n,
99            have: self.data.len(),
100            bt: Backtrace::capture(),
101        })?;
102        self.read += n;
103        Ok(())
104    }
105
106    pub fn read_chunk<E: Error + From<ReadError>, T>(
107        &mut self,
108        big: bool,
109        f: impl FnOnce(&mut DataRead) -> Result<T, E>,
110    ) -> Result<T, E> {
111        let len = if big {
112            self.read_u32()? as usize
113        } else {
114            self.read_u16()? as usize
115        };
116        let rb4 = self.read;
117        let r = f(self);
118        let read = self.read - rb4;
119        match r {
120            Err(e) => {
121                // skip this chunk
122                assert!(len >= read, "overread; supposed to read {len}; read {read}");
123                let n = len - read;
124                if n != 0 {
125                    #[cfg(debug_assertions)]
126                    println!("supposed to read {len}; read {read} - skipping excess");
127                    self.skip(n)?;
128                };
129                Err(e)
130            }
131            Ok(v) => {
132                debug_assert!(len >= read, "overread; supposed to read {len}; read {read}");
133                debug_assert!((len - read) == 0, "supposed to read {len}; read {read}");
134                Ok(v)
135            }
136        }
137    }
138
139    pub fn read_vec(&mut self, dst: &mut Vec<u8>, len: usize) -> Result<(), ReadError> {
140        dst.extend_from_slice(self.eat(len)?);
141        Ok(())
142    }
143
144    pub fn read_map(&mut self, dst: &mut HashMap<String, String>) -> Result<(), ReadError> {
145        let n = self.read_u8()?;
146        for _ in 0..n {
147            let key = self.read_utf()?;
148            let value = self.read_utf()?;
149            dst.insert(key.to_owned(), value.to_owned());
150        }
151        Ok(())
152    }
153
154    pub fn deflate(&mut self) -> Result<Vec<u8>, DecompressError> {
155        let mut dec = Decompress::new(true);
156        let mut raw = Vec::with_capacity(1024);
157        loop {
158            let t_in = dec.total_in();
159            let t_out = dec.total_out();
160            let res = dec.decompress_vec(self.data, &mut raw, FlushDecompress::None)?;
161            if dec.total_in() > t_in {
162                // we have to advance input every time, decompress_vec only knows the output position
163                self.data = &self.data[(dec.total_in() - t_in) as usize..];
164            }
165            match res {
166                // there's no more input (and the flush mode says so), we need to reserve additional space
167                Status::Ok | Status::BufError => {}
168                // input was already at the end, so this is referring to the output
169                Status::StreamEnd => break,
170            }
171            if dec.total_in() == t_in && dec.total_out() == t_out {
172                // protect against looping forever
173                return Err(DecompressError::DecompressStall);
174            }
175            raw.reserve(1024);
176        }
177        assert_eq!(dec.total_out() as usize, raw.len());
178        self.read = 0;
179        Ok(raw)
180    }
181}
182
183#[derive(Debug, Error)]
184pub enum DecompressError {
185    #[error("zlib decompression failed")]
186    Decompress(#[from] DError),
187    #[error("decompressor stalled before completion")]
188    DecompressStall,
189}
190
191#[derive(Debug, Error)]
192pub enum ReadError {
193    #[error("buffer underflow (expected {need} but got {have}) ({bt})")]
194    Underflow {
195        need: usize,
196        have: usize,
197
198        bt: Backtrace,
199    },
200    #[error("expected {0}")]
201    Expected(&'static str),
202    #[error("malformed utf8 in string")]
203    Utf8 {
204        #[from]
205        source: Utf8Error,
206    },
207}
208
209impl PartialEq for ReadError {
210    fn eq(&self, _: &Self) -> bool {
211        false
212    }
213}
214
215enum WriteBuff<'d> {
216    // unlike the DataRead want to access the written region after
217    Ref { raw: &'d mut [u8], pos: usize },
218    Vec(Vec<u8>),
219}
220
221impl<'d> WriteBuff<'d> {
222    fn check_capacity(&self, need: usize) -> Result<(), WriteError> {
223        match self {
224            Self::Ref { raw, pos } if raw.len() - pos < need => Err(WriteError::Overflow {
225                need,
226                have: raw.len() - pos,
227            }),
228            _ => Ok(()),
229        }
230    }
231
232    fn write(&mut self, data: &[u8]) {
233        match self {
234            Self::Ref { raw, pos } => {
235                let end = *pos + data.len();
236                raw[*pos..end].copy_from_slice(data);
237                *pos += data.len();
238            }
239            Self::Vec(v) => v.extend_from_slice(data),
240        }
241    }
242}
243
244pub struct DataWrite<'d> {
245    data: WriteBuff<'d>,
246}
247
248macro_rules! make_write {
249    ($name:ident, $type:ty) => {
250        pub fn $name(&mut self, val: $type) -> Result<(), WriteError> {
251            const LEN: usize = std::mem::size_of::<$type>();
252            self.data.check_capacity(LEN)?;
253            self.data.write(&<$type>::to_be_bytes(val));
254            Ok(())
255        }
256    };
257}
258
259impl<'d> DataWrite<'d> {
260    pub fn write_bool(&mut self, val: bool) -> Result<(), WriteError> {
261        self.write_u8(u8::from(val))
262    }
263
264    make_write!(write_u8, u8);
265    make_write!(write_i8, i8);
266    make_write!(write_u16, u16);
267    make_write!(write_i16, i16);
268    make_write!(write_u32, u32);
269    make_write!(write_i32, i32);
270    make_write!(write_f32, f32);
271    make_write!(write_u64, u64);
272    make_write!(write_i64, i64);
273    make_write!(write_f64, f64);
274
275    pub fn write_utf(&mut self, val: &str) -> Result<(), WriteError> {
276        if val.len() > u16::MAX as usize {
277            return Err(WriteError::TooLong { len: val.len() });
278        }
279        self.data.check_capacity(2 + val.len())?;
280        self.data.write(&u16::to_be_bytes(val.len() as u16));
281        self.data.write(val.as_bytes());
282        Ok(())
283    }
284
285    pub fn write_bytes(&mut self, val: &[u8]) -> Result<(), WriteError> {
286        self.data.check_capacity(val.len())?;
287        self.data.write(val);
288        Ok(())
289    }
290
291    #[must_use]
292    pub const fn is_owned(&self) -> bool {
293        matches!(self.data, WriteBuff::Vec(..))
294    }
295
296    #[must_use]
297    pub fn get_written(&self) -> &[u8] {
298        match &self.data {
299            WriteBuff::Ref { raw, pos } => &raw[..*pos],
300            WriteBuff::Vec(v) => v,
301        }
302    }
303
304    /// eat this datawrite
305    ///
306    /// panics if ref write buffer
307    #[must_use]
308    pub fn consume(self) -> Vec<u8> {
309        match self.data {
310            WriteBuff::Vec(v) => v,
311            WriteBuff::Ref { .. } => unreachable!(),
312        }
313    }
314
315    pub fn inflate(self, to: &mut DataWrite) -> Result<(), CompressError> {
316        // compress into the provided buffer
317        let WriteBuff::Vec(raw) = self.data else {
318            unreachable!("write buffer not owned")
319        };
320        let mut comp = Compress::new(Compression::default(), true);
321        // compress the immediate buffer into a temp buffer to copy it to buff? no thanks
322        match &mut to.data {
323            WriteBuff::Ref { raw: dst, pos } => {
324                match comp.compress(&raw, &mut dst[*pos..], FlushCompress::Finish)? {
325                    // there's no more input (and the flush mode says so), but we can't resize the output
326                    Status::Ok | Status::BufError => {
327                        return Err(CompressError::CompressEof(
328                            raw.len() - comp.total_in() as usize,
329                        ));
330                    }
331                    Status::StreamEnd => (),
332                }
333            }
334            WriteBuff::Vec(dst) => {
335                let mut input = raw.as_ref();
336                dst.reserve(1024);
337                loop {
338                    let t_in = comp.total_in();
339                    let t_out = comp.total_out();
340                    let res = comp.compress_vec(input, dst, FlushCompress::Finish)?;
341                    if comp.total_in() > t_in {
342                        // we have to advance input every time, compress_vec only knows the output position
343                        input = &input[(comp.total_in() - t_in) as usize..];
344                    }
345                    match res {
346                        // there's no more input (and the flush mode says so), we need to reserve additional space
347                        Status::Ok | Status::BufError => (),
348                        // input was already at the end, so this is referring to the output
349                        Status::StreamEnd => break,
350                    }
351                    if comp.total_in() == t_in && comp.total_out() == t_out {
352                        // protect against looping forever
353                        return Err(CompressError::CompressStall);
354                    }
355                    dst.reserve(1024);
356                }
357            }
358        }
359        assert_eq!(comp.total_in() as usize, raw.len());
360        Ok(())
361    }
362}
363
364impl Default for DataWrite<'static> {
365    fn default() -> Self {
366        Self {
367            data: WriteBuff::Vec(Vec::new()),
368        }
369    }
370}
371
372#[derive(Debug, Error)]
373pub enum CompressError {
374    #[error(transparent)]
375    Compress(#[from] CError),
376    #[error("compression overflow with {0} bytes of input remaining")]
377    CompressEof(usize),
378    #[error("compressor stalled before completion")]
379    CompressStall,
380}
381
382#[derive(Debug, Error)]
383pub enum WriteError {
384    #[error("buffer overflow (expected {need} but got {have})")]
385    Overflow { need: usize, have: usize },
386    #[error("string too long ({len} bytes of {})", u16::MAX)]
387    TooLong { len: usize },
388}
389
390impl PartialEq for WriteError {
391    fn eq(&self, _: &Self) -> bool {
392        false
393    }
394}
395
396impl<'d> From<&'d mut [u8]> for DataWrite<'d> {
397    fn from(value: &'d mut [u8]) -> Self {
398        Self {
399            data: WriteBuff::Ref { raw: value, pos: 0 },
400        }
401    }
402}
403
404impl From<Vec<u8>> for DataWrite<'static> {
405    fn from(value: Vec<u8>) -> Self {
406        Self {
407            data: WriteBuff::Vec(value),
408        }
409    }
410}
411
412impl<'d> TryFrom<DataWrite<'d>> for Vec<u8> {
413    type Error = ();
414
415    fn try_from(value: DataWrite<'d>) -> Result<Self, Self::Error> {
416        match value.data {
417            WriteBuff::Vec(v) => Ok(v),
418            WriteBuff::Ref { .. } => Err(()),
419        }
420    }
421}
422/// basic serialization/deserialization functions
423pub trait Serializable
424where
425    Self: Sized,
426{
427    type ReadError;
428    type WriteError;
429    /// deserialize self from a binary buffer
430    fn deserialize(buff: &mut DataRead<'_>) -> Result<Self, Self::ReadError>;
431    /// transform self into a binary buffer
432    fn serialize(&self, buff: &mut DataWrite<'_>) -> Result<(), Self::WriteError>;
433}
434
435#[derive(Clone, Copy, Eq, PartialEq, Default)]
436pub struct GridPos(pub usize, pub usize);
437
438impl From<u32> for GridPos {
439    fn from(value: u32) -> Self {
440        GridPos((value >> 16) as u16 as usize, value as u16 as usize)
441    }
442}
443
444impl From<GridPos> for u32 {
445    /// ```
446    /// # use mindus::data::GridPos;
447    /// assert_eq!(GridPos::from(u32::from(GridPos(1000, 5))), GridPos(1000, 5));
448    /// ```
449    fn from(value: GridPos) -> Self {
450        ((value.0 << 16) | value.1) as u32
451    }
452}
453
454impl fmt::Debug for GridPos {
455    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456        write!(f, "({0}, {1})", self.0, self.1)
457    }
458}
459
460#[cfg(test)]
461mod test {
462    use super::*;
463
464    #[test]
465    fn read() {
466        let mut read = DataRead::new("Thé qûick ઉrown fox 🦘 over\0\rthe lazy dog.".as_bytes());
467        assert_eq!(read.read_u8(), Ok(84));
468        assert_eq!(read.read_i8(), Ok(104));
469        assert_eq!(read.read_i8(), Ok(-61));
470        assert_eq!(read.read_u16(), Ok(43296));
471        assert_eq!(read.read_i16(), Ok(29123));
472        assert_eq!(read.read_i16(), Ok(-17559));
473        assert_eq!(read.read_i32(), Ok(1_667_965_152));
474        assert_eq!(read.read_i32(), Ok(-1_433_832_849));
475        assert_eq!(read.read_i64(), Ok(8_605_851_562_280_493_296));
476        assert_eq!(read.read_i64(), Ok(-6_942_694_510_468_635_278));
477        assert_eq!(read.read_utf(), Ok("the lazy dog."));
478    }
479
480    #[test]
481    fn write() {
482        let mut write = DataWrite::default();
483        assert_eq!(write.write_u8(84), Ok(()));
484        assert_eq!(write.write_i8(104), Ok(()));
485        assert_eq!(write.write_i8(-61), Ok(()));
486        assert_eq!(write.write_u16(43296), Ok(()));
487        assert_eq!(write.write_i16(29123), Ok(()));
488        assert_eq!(write.write_i16(-17559), Ok(()));
489        assert_eq!(write.write_i32(1_667_965_152), Ok(()));
490        assert_eq!(write.write_i32(-1_433_832_849), Ok(()));
491        assert_eq!(write.write_i64(8_605_851_562_280_493_296), Ok(()));
492        assert_eq!(write.write_i64(-6_942_694_510_468_635_278), Ok(()));
493        assert_eq!(write.write_utf("the lazy dog."), Ok(()));
494        assert_eq!(
495            write.get_written(),
496            "Thé qûick ઉrown fox 🦘 over\0\rthe lazy dog.".as_bytes()
497        );
498    }
499}