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