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