mindus/data/
map.rs

1//! the map module
2//! ### format
3//! note: utf = `len<u16>` + `utf8(read(len))`
4//!
5//! note: each section has a `u32` denoting its length
6//!
7//! key: `: T` and `x<T>` both mean read T, `iterate T` means iterate `read_T()` times
8//!
9//! ZLIB compressed stream contains:
10//! - header: 4b = `MSCH` [`MapReader::header`]
11//! - version: `u32` (should be 7+) [`MapReader::version`]
12//! - tag section `<u32>` [`MapReader::tags`]
13//!     - 1 byte of idk (skip)
14//!     - string map (`u16` for map len, iterate each, read `utf`)
15//! - content header section `<u32>`: [`MapReader::content`] (note: if map v8, will use [`BlockEnum`] and skip reading this)
16//!     - iterate `i8` (should = `10`)'
17//!         - the type: `i8` (0: item, block: 1, liquid: 4, status: 5, unit: 6, weather: 7, sector: 9, planet: 13
18//!         - item count: `u16` (item: 22, block: 422, liquid: 11, status: 21, unit: 66, weather: 6, sector: 35, planet: 7)
19//!         - these types all have their own modules: [`item`], [`content`], [`fluid`], [`modifier`], [`mod@unit`], [`weather`], [`sector`], [`planet`]
20//!         - iterate `u16`
21//!             - name: `utf`
22//! - map section `<u32>` [`MapReader::map`]
23//!     - width: `u16`, height: `u16`
24//!     - floor and tiles:
25//!         - for `i` in `w * h`
26//!             - `x = i % w`, `y = i / w`
27//!             - floor id: `u16`
28//!             - overlay id: `u16`
29//!             - consecutives: `u8`
30//!             - iterate `(i + 1)..(i + 1 + consecutives)`
31//!                 - `x = j % w`, `y = j / w`
32//!             - i += consecutives
33//!     - blocks
34//!         - for `i` in `w * h`
35//!             - block id: `u16`
36//!             - packed?: `i8`
37//!             - entity = `(packed & 1) not 0`
38//!             - data = `(packed & 2) not 0`
39//!             - newdata = `(packed & 3) not 0`
40//!             - if entity: central: `bool`
41//!             - if entity:
42//!                 - if central:
43//!                     - chunk len: `u16`
44//!                     - if block == building:
45//!                         - revision: `i8`
46//!                         - [`Build::read`]
47//!                     - else skip `chunk len`
48//!                 - or data
49//!                     - data: `i8`
50//!                 - or new data
51//!                     - data: `i8`
52//!                     - floor: `i8`
53//!                     - overlay: `i8`
54//!                     - extra: `i32`
55//!                 - else
56//!                     - consecutives: `u8`
57//!                     - iterate `(i + 1)..(i + 1 + consecutives)`
58//!                         - same block
59//!                     - i += consecutives
60//! - entities section `<u32>` [`MapReader::entities`]
61//!     - entity mapping
62//!         - iterate `u16`
63//!             - id: `i16`, name: `utf`
64//!     - team build plans
65//!         - for t in `teams<u32>`
66//!             - team = `team#<u32>`
67//!             - iterate `plans<u32>`
68//!                 - x: `u16`, y: `u16`, rot: `u16`, id: `u16`
69//!                 - o: `DynData` (refer to [`DynSerializer`])
70//!         - world entities
71//!             - iterate `u32`
72//!                 - len: `u16`
73//!                 - type: `u8`
74//!                 - if !mapping\[type\]
75//!                     - skip(len - 1)
76//!                     - continue
77//!                 - id: `u32`
78//!                 - entity read
79//! - markers section (v8)
80use fimg::DynImage;
81use std::collections::HashMap;
82use std::ops::CoroutineState::*;
83use std::ops::{Coroutine, Index, IndexMut};
84use std::pin::Pin;
85use std::sync::atomic::{AtomicU64, Ordering};
86use thiserror::Error;
87
88use crate::block::content::Type as BlockEnum;
89use crate::block::{Block, Rotation, State};
90use crate::data::DataRead;
91use crate::data::dynamic::DynData;
92use crate::data::renderer::*;
93use crate::fluid::Type as Fluid;
94use crate::item::{Type as Item, storage::Storage};
95use crate::team::Team;
96use crate::unit::Unit;
97#[cfg(doc)]
98use crate::{block::content, data::*, fluid, item, modifier, unit};
99
100use super::{Serializable, entity_mapping};
101use crate::content::Content;
102
103/// a tile in a map
104#[derive(Clone)]
105pub struct Tile {
106    pub floor: BlockEnum,
107    pub ore: BlockEnum,
108    build: Option<Build>,
109    pub nd: [u8; 7],
110}
111
112pub fn table(x: BlockEnum, scale: Scale) -> DynImage<&'static [u8]> {
113    FLOOR_TABLE[x as usize][mcg() as usize][scale as usize]
114}
115
116pub fn mcg() -> u64 {
117    static STATE: AtomicU64 = AtomicU64::new(25);
118    let s = STATE.load(Ordering::Relaxed);
119    // a * s % m
120    let s = (s.wrapping_mul(0xecc5)) % (1 << 32);
121    STATE.store(s, Ordering::Relaxed);
122    (s >> 13) & (0b111)
123}
124
125static FLOOR_TABLE: [[[DynImage<&'static [u8]>; 3]; 8]; BlockEnum::ALL.len()] = {
126    let mut table = [[car::map!(load!("empty"), DynImage::from); 8]; BlockEnum::ALL.len()];
127    macro_rules! image {
128        (| $($x:literal $(|)?)+) => { paste::paste! {
129            $(table[BlockEnum::[<$x:camel>] as usize] = load!(8x $x));+
130        } };
131        (| single $($x:literal $(|)?)+) => { paste::paste! {
132            $(table[BlockEnum::[<$x:camel>] as usize] =
133                car::map!([load!($x); 8], |x| car::map!(x, DynImage::from)));+
134        } }
135    }
136
137    image! [
138        | single "colored-floor" | "colored-wall" | "metal-tiles-1" | "metal-tiles-2" | "metal-tiles-3" | "metal-tiles-4" | "metal-tiles-5" | "metal-tiles-6" | "metal-tiles-7" | "metal-tiles-8" | "metal-tiles-9" | "metal-tiles-10" | "metal-tiles-11" | "metal-tiles-12"
139        | "metal-floor" | "metal-floor-2" | "metal-floor-3" | "metal-floor-4" | "metal-floor-5"
140        | "dark-panel-1" | "dark-panel-2" | "dark-panel-3" | "dark-panel-4" | "dark-panel-5" | "dark-panel-6"
141        | "arkycite-floor"
142        | "darksand-tainted-water" | "darksand-water" | "deep-tainted-water" | "deep-water" | "sand-water" | "shallow-water" | "tainted-water"
143        | "tar" | "pooled-cryofluid"
144        | "space"
145        | "stone-vent"
146        | "core-zone"
147    ];
148    image! {
149        | "ore-copper" | "ore-beryllium" | "ore-lead" | "ore-scrap" | "ore-coal" | "ore-thorium" | "ore-titanium" | "ore-tungsten" | "pebbles" | "tendrils" | "ore-wall-tungsten" | "ore-wall-beryllium" | "ore-wall-thorium" | "ore-crystal-thorium"
150        | "yellow-stone-vent" | "arkyic-vent" | "crystalline-vent"  | "red-stone-vent" | "carbon-vent"
151        | "darksand"
152        | "sand-floor"
153        | "dacite"
154        | "dirt"
155        | "basalt" | "basalt-vent"
156        | "moss"
157        | "mud"
158        | "ice-snow" | "snow" | "salt" | "ice"
159        | "hotrock" | "char" | "magmarock" | "molten-slag"
160        | "shale"
161        | "metal-floor-damaged"
162        | "stone"
163        | "bluemat"
164        | "ferric-craters"
165        | "beryllic-stone"
166        | "grass"
167        | "rhyolite" | "rough-rhyolite" | "rhyolite-crater" | "rhyolite-vent"
168        | "crater-stone"
169        | "redmat"
170        | "red-ice"
171        | "spore-moss"
172        | "regolith"
173        | "ferric-stone"
174        | "arkyic-stone"
175        | "yellow-stone" | "yellow-stone-plates"
176        | "red-stone" | "dense-red-stone"
177        | "carbon-stone"
178        | "crystal-floor" | "crystalline-stone"
179    };
180    table
181};
182
183impl Tile {
184    #[must_use]
185    pub const fn new(floor: BlockEnum, ore: BlockEnum) -> Self {
186        Self {
187            floor,
188            ore,
189            build: None,
190            nd: [0; 7],
191        }
192    }
193
194    fn set_block(&mut self, block: &'static Block) {
195        self.build = Some(Build {
196            block,
197            state: None,
198            items: Storage::new(),
199            liquids: Storage::new(),
200            rotation: Rotation::Up,
201            team: Team::SHARDED,
202            data: 0,
203        });
204    }
205
206    #[must_use]
207    pub const fn build(&self) -> Option<&Build> {
208        self.build.as_ref()
209    }
210
211    /// size of this tile
212    ///
213    /// ._.
214    ///
215    /// dont think about it too much
216    #[must_use]
217    #[inline]
218    pub fn size(&self) -> u8 {
219        self.build.as_ref().map_or(1, |v| v.block.get_size())
220    }
221
222    #[must_use]
223    #[inline]
224    pub fn has_ore(&self) -> bool {
225        self.ore != BlockEnum::Air
226    }
227
228    /// Draw this tiles build.
229    #[must_use]
230    #[inline]
231    pub fn build_image(
232        &self,
233        context: Option<&RenderingContext>,
234        s: Scale,
235    ) -> DynImage<image::Cow> {
236        // building covers floore
237        let Some(b) = &self.build else {
238            unreachable!();
239        };
240        b.image(context, s)
241    }
242}
243
244impl std::fmt::Debug for Tile {
245    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246        write!(
247            f,
248            "Tile@{}{}{}",
249            self.floor.get_name(),
250            if self.ore != BlockEnum::Air {
251                format!("+{}", self.ore.get_name())
252            } else {
253                String::new()
254            },
255            if let Some(build) = &self.build {
256                format!(":{}", build.block.name())
257            } else {
258                String::new()
259            }
260        )
261    }
262}
263
264impl BlockState for Tile {
265    fn get_block(&self) -> Option<&'static Block> {
266        Some(self.build()?.block)
267    }
268}
269
270impl RotationState for Tile {
271    fn get_rotation(&self) -> Option<Rotation> {
272        Some(self.build()?.rotation)
273    }
274}
275
276impl RotationState for Option<Tile> {
277    fn get_rotation(&self) -> Option<Rotation> {
278        self.as_ref().unwrap().get_rotation()
279    }
280}
281
282impl BlockState for Option<Tile> {
283    fn get_block(&self) -> Option<&'static Block> {
284        self.as_ref().unwrap().get_block()
285    }
286}
287
288/// a build on a tile in a map
289#[derive(Clone)]
290pub struct Build {
291    pub block: &'static Block,
292    pub items: Storage<Item>,
293    pub liquids: Storage<Fluid>,
294    pub state: Option<State>,
295    // pub health: f32,
296    pub rotation: Rotation,
297    pub team: Team,
298    pub data: i8,
299}
300
301impl std::fmt::Debug for Build {
302    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
303        write!(f, "Build<{block}>", block = self.block.name(),)
304    }
305}
306
307impl Build {
308    #[must_use]
309    pub fn new(block: &'static Block) -> Build {
310        Self {
311            block,
312            items: Storage::default(),
313            liquids: Storage::default(),
314            state: None,
315            rotation: Rotation::Up,
316            team: Team::SHARDED,
317            data: 0,
318        }
319    }
320
321    fn image(&self, context: Option<&RenderingContext>, s: Scale) -> DynImage<image::Cow> {
322        self.block
323            .image(self.state.as_ref(), context, self.rotation, s)
324    }
325
326    #[must_use]
327    pub const fn name(&self) -> &str {
328        self.block.name()
329    }
330
331    pub fn read(&mut self, buff: &mut DataRead<'_>) -> Result<(), ReadError> {
332        // health
333        let _ = buff.read_f32()?;
334        let rot = buff.read_i8()? as i16;
335        // team
336        let _ = buff.read_i8()?;
337        self.rotation = Rotation::try_from((rot & 127) as u8).unwrap_or(Rotation::Up);
338        let mut mask = 0;
339        let mut version = 0;
340        if rot & 128 != 0 {
341            version = buff.read_u8()?;
342            if version < 3 {
343                return Err(ReadError::Version(version));
344            }
345            buff.skip(1)?;
346            mask = buff.read_u8()?;
347        }
348
349        if mask & 1 != 0 {
350            read_items(buff, &mut self.items)?;
351        }
352        if mask & 2 != 0 {
353            read_power(buff)?;
354        }
355        if mask & 4 != 0 {
356            read_liquids(buff, &mut self.liquids)?;
357        }
358        // "efficiency"
359        _ = buff.read_u8()? as f64 / 255.;
360        _ = buff.read_u8()? as f64 / 255.;
361
362        if version == 4 {
363            // visible flags for fog
364            _ = buff.read_u64()?;
365        }
366        // "overridden by subclasses"
367        self.block.read(self, buff)?;
368
369        Ok(())
370    }
371}
372
373/// format:
374/// - iterate [`u16`]
375///     - item: [`u16`] as [`Item`]
376///     - amount: [`u32`]
377///
378fn read_items(from: &mut DataRead, to: &mut Storage<Item>) -> Result<(), ReadError> {
379    to.clear();
380    let n = from.read_u16()?;
381    to.reserve(n as usize);
382    for _ in 0..n {
383        let item = from.read_u16()?;
384        let amount = from.read_u32()?;
385        if let Ok(item) = Item::try_from(item) {
386            to.set(item, amount);
387        }
388    }
389    Ok(())
390}
391
392/// format:
393/// - iterate [`u16`]
394///     - liquid: [`u16`] as [`Fluid`]
395///     - amount: [`f32`]
396fn read_liquids(from: &mut DataRead, to: &mut Storage<Fluid>) -> Result<(), ReadError> {
397    to.clear();
398    let n = from.read_u16()?;
399    to.reserve(n as usize);
400    for _ in 0..n {
401        let fluid = from.read_u16()?;
402        let amount = from.read_f32()?;
403        if let Ok(fluid) = Fluid::try_from(fluid) {
404            to.set(fluid, (amount * 100.0) as u32);
405        }
406    }
407    Ok(())
408}
409
410/// format:
411/// - iterate [`u16`]
412///     - link: [`i32`]
413/// - status: [`f32`]
414fn read_power(from: &mut DataRead) -> Result<(), ReadError> {
415    let n = from.read_u16()? as usize;
416    from.skip((n + 1) * 4)?;
417    Ok(())
418}
419
420#[test]
421fn test_read_items() {
422    let mut s = Storage::new();
423    read_items(
424        &mut DataRead::new(&[
425            0, 6, 0, 0, 0, 0, 2, 187, 0, 1, 0, 0, 1, 154, 0, 2, 0, 0, 15, 160, 0, 3, 0, 0, 0, 235,
426            0, 6, 0, 0, 1, 46, 0, 12, 0, 0, 1, 81, 255, 255,
427        ]),
428        &mut s,
429    )
430    .unwrap();
431    assert!(s.get_total() == 5983);
432}
433
434#[test]
435fn test_read_liquids() {
436    let mut s = Storage::new();
437    read_liquids(
438        &mut DataRead::new(&[0, 1, 0, 0, 67, 111, 247, 126, 255, 255]),
439        &mut s,
440    )
441    .unwrap();
442    assert!(s.get(Fluid::Water) == 23996);
443}
444
445/// a map.
446/// ## Does not support serialization yet!
447#[derive(Debug)]
448pub struct Map {
449    pub width: usize,
450    pub height: usize,
451    pub tags: HashMap<String, String>,
452    pub entities: Vec<Unit>,
453    /// row major 2d array
454    /// ```rs
455    /// (0, 0), (1, 0), (2, 0)
456    /// (0, 1), (1, 1), (2, 1)
457    /// (0, 2), (1, 2), (2, 2)
458    /// ```
459    pub tiles: Vec<Tile>,
460}
461
462impl Map {
463    #[lower::apply(wrapping)]
464    pub(crate) fn cross(&self, j: usize) -> [Option<&Tile>; 4] {
465        [
466            self.tiles.get(j + self.width),
467            self.tiles.get(j + 1),
468            self.tiles.get(j - self.width),
469            self.tiles.get(j - 1),
470        ]
471    }
472    #[lower::apply(wrapping)]
473    pub(crate) fn corners(&self, j: usize) -> [Option<&Tile>; 4] {
474        [
475            self.tiles.get(j - self.width - 1),
476            self.tiles.get(j - self.width + 1),
477            self.tiles.get(j + self.width - 1),
478            self.tiles.get(j + self.width + 1),
479        ]
480    }
481
482    #[must_use]
483    pub fn new(width: usize, height: usize, tags: HashMap<String, String>) -> Self {
484        Self {
485            tiles: Vec::with_capacity(width * height),
486            height,
487            width,
488            tags,
489            entities: vec![],
490        }
491    }
492
493    fn push(&mut self, t: Tile) {
494        self.tiles.push(t);
495    }
496}
497
498impl Index<usize> for Map {
499    type Output = Tile;
500    fn index(&self, index: usize) -> &Self::Output {
501        &self.tiles[index]
502    }
503}
504
505impl IndexMut<usize> for Map {
506    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
507        &mut self.tiles[index]
508    }
509}
510
511const MAP_HEADER: [u8; 4] = [b'M', b'S', b'A', b'V'];
512
513/// error occurring when reading a map fails
514#[derive(Debug, Error)]
515pub enum ReadError {
516    #[error("failed to read from buffer")]
517    Read(#[from] super::ReadError),
518    #[error(transparent)]
519    Decompress(#[from] super::DecompressError),
520    #[error("incorrect header ({0:?})")]
521    Header([u8; 4]),
522    #[error("unsupported version ({0})")]
523    Version(u8),
524    #[error("unknown block at index {0:?}")]
525    NoSuchBlock(u16),
526    #[error("no block {0}")]
527    NoBlockFound(String),
528    #[error("failed to read block data")]
529    ReadState(#[from] super::dynamic::ReadError),
530    #[error("no block, but data")]
531    NoBlockWithData,
532}
533
534/// Struct for granular map deserialization.
535pub struct MapReader {
536    #[allow(dead_code)]
537    backing: Vec<u8>,
538    // dataread references 'backing'
539    buff: DataRead<'static>,
540}
541
542#[derive(Debug)]
543pub enum ThinBloc {
544    Build(Rotation, &'static Block, Team),
545    Many(Option<&'static Block>, u8),
546}
547
548#[derive(Debug)]
549pub enum ThinMapData {
550    Init { width: u16, height: u16 },
551    Bloc(ThinBloc),
552    Tile { floor: BlockEnum, ore: BlockEnum },
553}
554
555#[derive(Debug)]
556pub enum Bloc {
557    Build(Build, &'static Block),
558    Data(&'static Block, i8),
559    Nd(Option<&'static Block>, [u8; 7]),
560    Many(Option<&'static Block>, u8),
561}
562
563#[derive(Debug)]
564pub enum MapData {
565    Init { width: u16, height: u16 },
566    Bloc(Bloc),
567    Tile { floor: BlockEnum, ore: BlockEnum },
568}
569
570#[derive(Debug)]
571pub enum EntityData {
572    Length(u32),
573    Data(Unit),
574}
575
576macro_rules! tiles {
577    ($count:ident, $me:ident, $w: ident,$r:ident) => {
578        let mut i = 0;
579        while i < $count {
580            let floor_id = $me.buff.read_u16()?;
581            let overlay_id = $me.buff.read_u16()?;
582            let &floor = $r.get(floor_id as usize).unwrap_or(&BlockEnum::Stone);
583            let &ore = $r.get(overlay_id as usize).unwrap_or(&BlockEnum::Air);
584            yield $w::Tile { floor, ore };
585            let consecutives = $me.buff.read_u8()? as usize;
586            for _ in 0..consecutives {
587                yield $w::Tile { floor, ore };
588            }
589            i += consecutives;
590            i += 1;
591        }
592    };
593}
594
595pub type Registrar = [BlockEnum; BlockEnum::ALL.len()];
596impl MapReader {
597    pub fn new(buff: &mut DataRead<'_>) -> Result<Self, ReadError> {
598        let backing = buff.deflate()?;
599        Ok(Self {
600            buff: DataRead::new(unsafe {
601                std::mem::transmute::<&'_ [u8], &'static [u8]>(&backing)
602            }),
603            backing,
604        })
605    }
606
607    pub fn header(&mut self) -> Result<[u8; 4], ReadError> {
608        let b = self.buff.readN::<4>()?;
609        (b == MAP_HEADER).then_some(b).ok_or(ReadError::Header(b))
610    }
611
612    pub fn version(&mut self) -> Result<u32, ReadError> {
613        let x = self.buff.read_u32()?;
614        (7..=9)
615            .contains(&x)
616            .then_some(x)
617            .ok_or(ReadError::Version(x.try_into().unwrap_or(0)))
618    }
619
620    pub fn tags(&mut self) -> Result<HashMap<&str, &str>, ReadError> {
621        let mut tags = HashMap::new();
622        self.buff.skip(5)?;
623        for _ in 0..self.buff.read_u8()? {
624            let key = self.buff.read_utf()?;
625            let value = self.buff.read_utf()?;
626            tags.insert(key, value);
627        }
628        Ok(tags)
629    }
630
631    pub fn content(&mut self) -> Result<Registrar, ReadError> {
632        let mut registrar = BlockEnum::ALL;
633        _ = self.buff.read_u32()?;
634        for _ in 0..self.buff.read_i8()? {
635            let ty = self.buff.read_u8()?;
636            for index in 0..self.buff.read_u16()? as usize {
637                if ty == 1 {
638                    let name = self.buff.read_utf()?;
639                    let block = BlockEnum::by_name(name)
640                        .ok_or_else(|| ReadError::NoBlockFound(name.to_string()))?;
641                    registrar.get_mut(index).map(|x| *x = block);
642                } else {
643                    let n = self.buff.read_u16()?;
644                    self.buff.skip(n as usize)?;
645                }
646            }
647        }
648        Ok(registrar)
649    }
650
651    pub fn tags_alloc(&mut self) -> Result<HashMap<String, String>, ReadError> {
652        let mut tags = HashMap::new();
653        self.buff
654            .read_chunk(true, |buff| {
655                buff.skip(1)?;
656                for _ in 0..buff.read_u8()? {
657                    let key = buff.read_utf()?;
658                    let value = buff.read_utf()?;
659                    tags.insert(key.to_owned(), value.to_owned());
660                }
661                Ok::<(), ReadError>(())
662            })
663            .map(|_| tags)
664    }
665
666    pub fn skip(&mut self) -> Result<(), ReadError> {
667        let len = self.buff.read_u32()? as usize;
668        self.buff.skip(len)?;
669        Ok(())
670    }
671
672    pub fn thin_map(
673        &mut self,
674        r: Registrar,
675    ) -> Result<
676        impl Coroutine<(), Return = Result<(), ReadError>, Yield = ThinMapData> + '_,
677        ReadError,
678    > {
679        let len = self.buff.read_u32()? as usize;
680        let rb4 = self.buff.read;
681        let map = #[coroutine]
682        move || {
683            let w = self.buff.read_u16()?;
684            let h = self.buff.read_u16()?;
685            yield ThinMapData::Init {
686                width: w,
687                height: h,
688            };
689            let w = w as usize;
690            let h = h as usize;
691            let count = w * h;
692            tiles!(count, self, ThinMapData, r);
693
694            let mut i = 0;
695            while i < count {
696                let block_id = self.buff.read_u16()?;
697                let packed = self.buff.read_u8()?;
698                let entity = (packed & 1) != 0;
699                let data = (packed & 2) != 0;
700                let newdata = (packed & 4) != 0;
701                if newdata {
702                    self.buff.skip(7)?;
703                }
704                let central = if entity {
705                    self.buff.read_bool()?
706                } else {
707                    false
708                };
709                let block = *r
710                    .get(block_id as usize)
711                    .ok_or(ReadError::NoSuchBlock(block_id))?;
712                let block = block.to_block();
713                yield if entity {
714                    if central {
715                        let len = self.buff.read_u16()? as usize;
716                        let rb4 = self.buff.read;
717
718                        #[cfg(debug_assertions)]
719                        println!("reading {block:?} ");
720                        let _ = self.buff.read_i8()?;
721                        let _ = self.buff.read_f32()?;
722                        let rot = self.buff.read_i8()?;
723                        let rot = Rotation::try_from((rot & 127) as u8).unwrap_or(Rotation::Up);
724                        let team = Team::of(self.buff.read_u8()?);
725                        let read = self.buff.read - rb4;
726                        let n = len - read;
727                        self.buff.skip(n)?;
728
729                        ThinMapData::Bloc(ThinBloc::Build(rot, block.unwrap(), team))
730                    } else {
731                        ThinMapData::Bloc(ThinBloc::Many(None, 0))
732                    }
733                } else if data {
734                    _ = self.buff.read_i8()?;
735                    ThinMapData::Bloc(ThinBloc::Many(block, 0))
736                } else if newdata {
737                    ThinMapData::Bloc(ThinBloc::Many(block, 0))
738                } else {
739                    let consecutives = self.buff.read_u8()?;
740                    i += consecutives as usize;
741                    ThinMapData::Bloc(ThinBloc::Many(block, consecutives))
742                };
743
744                i += 1;
745            }
746            let read = self.buff.read - rb4;
747            debug_assert!(len >= read, "overread; supposed to read {len}; read {read}");
748            debug_assert!((len - read) == 0, "supposed to read {len}; read {read}");
749            Ok(())
750        };
751        Ok(map)
752    }
753
754    pub fn collect_map(
755        &mut self,
756        tags: HashMap<String, String>,
757        r: Registrar,
758    ) -> Result<Map, ReadError> {
759        let mut co = self.map(r)?;
760        let (w, h) = match Pin::new(&mut co).resume(()) {
761            Yielded(MapData::Init { width, height }) => (width as usize, height as usize),
762            Complete(Err(x)) => return Err(x),
763            _ => unreachable!(),
764        };
765        let mut m = Map::new(w, h, tags);
766        for _ in 0..w * h {
767            match Pin::new(&mut co).resume(()) {
768                Yielded(MapData::Tile { floor, ore }) => m.push(Tile::new(floor, ore)),
769                Complete(Err(x)) => return Err(x),
770                _ => unreachable!(),
771            }
772        }
773        let mut i = 0;
774        while i < w * h {
775            match Pin::new(&mut co).resume(()) {
776                Yielded(MapData::Bloc(Bloc::Many(None, n))) => i += n as usize,
777                Yielded(MapData::Bloc(Bloc::Build(x, y))) => {
778                    m[i].set_block(y);
779                    m[i].build = Some(x);
780                }
781                Yielded(MapData::Bloc(Bloc::Data(x, y))) => {
782                    m[i].set_block(x);
783                    m[i].build.as_mut().unwrap().data = y;
784                }
785                Yielded(MapData::Bloc(Bloc::Many(Some(bloc), n))) => {
786                    for i in i..=i + n as usize {
787                        m[i].set_block(bloc);
788                    }
789                    i += n as usize;
790                }
791                Yielded(MapData::Bloc(Bloc::Nd(b, d))) => {
792                    b.map(|b| m[i].set_block(b));
793                    m[i].nd = d
794                }
795                Complete(Err(x)) => return Err(x),
796                _ => unreachable!(),
797            }
798            i += 1;
799        }
800        match Pin::new(&mut co).resume(()) {
801            Complete(Ok(())) => (),
802            _ => unreachable!(),
803        };
804        Ok(m)
805    }
806
807    pub fn map(
808        &mut self,
809        r: Registrar,
810    ) -> Result<impl Coroutine<(), Return = Result<(), ReadError>, Yield = MapData> + '_, ReadError>
811    {
812        let len = self.buff.read_u32()? as usize;
813        let rb4 = self.buff.read;
814
815        let c = #[coroutine]
816        move || {
817            let w = self.buff.read_u16()?;
818            let h = self.buff.read_u16()?;
819            yield MapData::Init {
820                width: w,
821                height: h,
822            };
823            let w = w as usize;
824            let h = h as usize;
825            let count = w * h;
826            tiles!(count, self, MapData, r);
827
828            let mut i = 0;
829            while i < count {
830                let block_id = self.buff.read_u16()?;
831                let packed = self.buff.read_u8()?;
832                let entity = (packed & 1) != 0;
833                let data = (packed & 2) != 0;
834                let newdata = (packed & 4) != 0;
835                let newdata = newdata.then(|| self.buff.readN::<7>()).transpose()?;
836                assert!(!data);
837                let central = if entity {
838                    self.buff.read_bool()?
839                } else {
840                    false
841                };
842                let block = r
843                    .get(block_id as usize)
844                    .ok_or(ReadError::NoSuchBlock(block_id))?;
845                let block = block.to_block();
846                yield if entity {
847                    if central {
848                        let block = block.ok_or(ReadError::NoBlockWithData)?;
849                        let len = self.buff.read_u16()? as usize;
850                        let rb4 = self.buff.read;
851
852                        #[cfg(debug_assertions)]
853                        println!("reading {block:?} ");
854                        let _ = self.buff.read_i8()?;
855                        let mut b = Build::new(block);
856                        b.read(&mut self.buff)?;
857                        // implementation not complete, skip remaining bytes (TODO finish impl)
858                        let read = self.buff.read - rb4;
859
860                        // skip this chunk
861                        assert!(len >= read, "overread; supposed to read {len}; read {read}");
862                        let n = len - read;
863                        if n != 0 {
864                            #[cfg(debug_assertions)]
865                            println!(
866                                "({block:?}) supposed to read {len}; read {read} - skipping excess"
867                            );
868                            self.buff.skip(n)?;
869                        };
870
871                        MapData::Bloc(Bloc::Build(b, block))
872                    } else {
873                        MapData::Bloc(Bloc::Many(None, 0))
874                    }
875                } else if data {
876                    MapData::Bloc(Bloc::Data(
877                        block.ok_or(ReadError::NoBlockWithData)?,
878                        self.buff.read_i8()?,
879                    ))
880                } else if let Some(nd) = newdata {
881                    MapData::Bloc(Bloc::Nd(block, nd))
882                } else {
883                    let consecutives = self.buff.read_u8()?;
884                    i += consecutives as usize;
885                    MapData::Bloc(Bloc::Many(block, consecutives))
886                };
887
888                i += 1;
889            }
890            let read = self.buff.read - rb4;
891            debug_assert!(len >= read, "overread; supposed to read {len}; read {read}");
892            debug_assert!((len - read) == 0, "supposed to read {len}; read {read}");
893            Ok(())
894        };
895        Ok(c)
896    }
897
898    pub fn entities(
899        &mut self,
900    ) -> Result<
901        impl Coroutine<(), Yield = EntityData, Return = Result<(), ReadError>> + '_,
902        ReadError,
903    > {
904        let len = self.buff.read_u32()? as usize;
905        let rb4 = self.buff.read;
906
907        let c = #[coroutine]
908        move || {
909            for _ in 0..self.buff.read_u16()? {
910                self.buff.skip(2)?;
911                let _ = self.buff.read_utf()?;
912            }
913            // read team block plans (ghosts) (SaveVersion.java#389)
914            for _ in 0..self.buff.read_u32()? {
915                self.buff.skip(4)?;
916                for _ in 0..self.buff.read_u32()? {
917                    self.buff.skip(8usize)?;
918                    let _ = DynData::deserialize(&mut self.buff)?;
919                }
920            }
921            // read world entities (#412). eg units
922            let n = self.buff.read_u32()?;
923            yield EntityData::Length(n);
924            for _ in 0..n {
925                let len = self.buff.read_u16()? as usize;
926                let rb4 = self.buff.read;
927                let id = self.buff.read_u8()? as usize;
928                let Some(&Some(u)) = entity_mapping::ID.get(id) else {
929                    self.buff.skip(len - 1)?;
930                    continue;
931                };
932                _ = self.buff.read_u32()?;
933                yield EntityData::Data(u.read(&mut self.buff)?);
934                let read = self.buff.read - rb4;
935                debug_assert!(len >= read, "overread; supposed to read {len}; read {read}");
936                let n = len - read;
937                if n != 0 && cfg!(debug_assertions) {
938                    dbg!("underread: skipping {n} bytes");
939                }
940                self.buff.skip(n)?;
941            }
942            let read = self.buff.read - rb4;
943            debug_assert!(len >= read, "overread; supposed to read {len}; read {read}");
944            debug_assert!((len - read) == 0, "supposed to read {len}; read {read}");
945            Ok(())
946        };
947        Ok(c)
948    }
949
950    pub fn collect_entities(&mut self) -> Result<Vec<Unit>, ReadError> {
951        let mut co = self.entities()?;
952        let n = match Pin::new(&mut co).resume(()) {
953            Yielded(EntityData::Length(x)) => x,
954            Complete(Err(e)) => return Err(e),
955            _ => unreachable!(),
956        };
957        let mut o = Vec::with_capacity(n as usize);
958        for _ in 0..n {
959            match Pin::new(&mut co).resume(()) {
960                Yielded(EntityData::Data(x)) => o.push(x),
961                Complete(Err(e)) => return Err(e),
962                Complete(Ok(())) => break,
963                _ => unreachable!(),
964            }
965        }
966        Ok(o)
967    }
968}
969
970/// serde map
971impl Serializable for Map {
972    type ReadError = ReadError;
973    type WriteError = ();
974    /// deserialize a map
975    ///
976    /// note: does not deserialize all data
977    fn deserialize(buff: &mut DataRead<'_>) -> Result<Map, Self::ReadError> {
978        let mut buff = MapReader::new(buff)?;
979        buff.header()?;
980        let v = buff.version()?;
981        let tags = buff.tags_alloc()?;
982        let r = buff.content()?;
983
984        let mut m = buff.collect_map(tags, r)?;
985        m.entities = buff.collect_entities()?;
986        if v == 8 {
987            // skip marker region
988            buff.skip()?;
989        }
990
991        // skip custom chunks
992        buff.skip()?;
993        Ok(m)
994    }
995
996    /// serialize a map (todo)
997    /// panics: always
998    fn serialize(&self, _: &mut super::DataWrite<'_>) -> Result<(), Self::WriteError> {
999        unimplemented!()
1000    }
1001}