ct_tilemap/
lib.rs

1#![warn(missing_docs)]
2#![warn(clippy::pedantic, clippy::perf, clippy::cargo)]
3#![allow(
4    clippy::cast_possible_truncation,
5    clippy::too_many_lines,
6    clippy::cast_lossless,
7    clippy::comparison_chain  // according to their docs, this is not always optimized reliably
8)]
9
10/*!
11Simple library to handle [Clickteam TileMap](https://github.com/clickteam-plugin/TileMap) files.
12
13```rust
14# use std::io::Cursor; use ct_tilemap::{Tile, TileMap, ReadError};
15# fn main() -> Result<(), ReadError> {
16#
17# struct TrashWriter;
18# impl std::io::Write for TrashWriter {
19#   fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {Ok(buf.len())}
20#   fn flush(&mut self) -> std::io::Result<()> {Ok(())}
21# }
22#
23let mut tilemap = TileMap::read(
24    /* .. */
25     # Cursor::new(b"ACHTUNG!\x05\x01TILE\x00\x00\x00\x00\x01\x00\xda\x89\x72\x08tiles.png")
26)?;
27
28for layer in tilemap.layers.iter_mut() {
29    layer.resize(8, 8);
30    layer[(0, 0)] = Tile {id: 0x1234};
31    layer[(0, 1)] = Tile {position: [5, 3]};
32    let sublayer = layer.add_sublayer(b"YES");
33    sublayer[(3, 3)].copy_from_slice(b"NO!");
34}
35
36tilemap.write(
37    /* .. */
38   # TrashWriter
39)?;
40#
41# Ok(())
42# }
43```
44 */
45
46use bytemuck::{cast_slice, Pod, Zeroable};
47use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
48use std::fmt::{Display, Formatter};
49use std::{
50    collections::HashMap,
51    io::{self, Cursor, Read, Write},
52    iter,
53    ops::{Index, IndexMut},
54};
55
56mod formatting;
57mod read_helper;
58mod write_helper;
59
60/// A representation of a tilemap file.
61#[derive(Clone, PartialEq, Default)]
62pub struct TileMap {
63    /// A collection of each layer of the tilemap.
64    /// Any more than 65536 layers will not be saved.
65    pub layers: Vec<Layer>,
66    /// A collection of the tilesets of the tilemap.
67    /// Any more than 256 tilesets will not be saved.
68    pub tilesets: Vec<TileSet>,
69    /// The dynamic properties of the tilemap.
70    /// Any more than 65536 properties will not be saved.
71    pub properties: HashMap<String, Property>,
72}
73
74/// A reason why reading a tilemap failed.
75pub enum ReadError {
76    /// IO error.
77    IoError(io::Error),
78    /// Invalid magic string.
79    InvalidMagic,
80    /// Unsupported version.
81    UnsupportedVersion(u16),
82    /// Invalid type in property map.
83    InvalidType(u8),
84    /// Layer length was not a multiple of two.
85    InvalidLayerLength,
86    /// Invalid header.
87    InvalidHeader(String),
88}
89impl From<io::Error> for ReadError {
90    fn from(err: io::Error) -> Self {
91        ReadError::IoError(err)
92    }
93}
94
95impl std::fmt::Debug for ReadError {
96    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
97        match self {
98            ReadError::IoError(err) => write!(f, "{err}"),
99            ReadError::UnsupportedVersion(v) => {
100                write!(f, "version {v} of tilemap files is not supported")
101            }
102            ReadError::InvalidType(ty) => {
103                write!(f, "found invalid type 0x{ty:02X} in property mapping")
104            }
105            ReadError::InvalidHeader(head) => write!(f, "found invalid header \"{head}\""),
106            ReadError::InvalidMagic => write!(f, "found invalid magic string for tilemap"),
107            ReadError::InvalidLayerLength => write!(f, "layer byte length did not match its size"),
108        }
109    }
110}
111
112impl Display for ReadError {
113    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
114        write!(f, "{self:?}")
115    }
116}
117
118impl std::error::Error for ReadError {}
119
120/// A helper struct to make writing headers easier.
121struct Header<'a, 'b, W: Write> {
122    stream: &'a mut W,
123    buffer: Cursor<Vec<u8>>,
124    header: &'b [u8],
125}
126
127impl<'a, 'b, W: Write> Header<'a, 'b, W> {
128    #[must_use = "header won't write if dropped"]
129    fn new(stream: &'a mut W, header: &'b [u8]) -> Self {
130        Header {
131            stream,
132            buffer: Cursor::new(Vec::new()),
133            header,
134        }
135    }
136
137    fn write_header(self) -> io::Result<()> {
138        self.stream.write_all(self.header)?;
139        self.stream
140            .write_all(&(self.buffer.get_ref().len() as u32).to_le_bytes())?;
141        self.stream.write_all(self.buffer.get_ref())
142    }
143}
144
145impl<'a, 'b, W: Write> Write for Header<'a, 'b, W> {
146    fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
147        self.buffer.write(buf)
148    }
149
150    fn flush(&mut self) -> Result<(), io::Error> {
151        Ok(())
152    }
153}
154
155impl TileMap {
156    /// Attempt to read a tilemap from a readable.
157    ///
158    /// # Errors
159    /// Errors if the file fails to be read.
160    pub fn read(mut cursor: impl Read) -> Result<Self, ReadError> {
161        // Read the magic string, see if it matches
162        let mut buf = [0; 8];
163        cursor.read_exact(&mut buf)?;
164        if &buf != b"ACHTUNG!" {
165            return Err(ReadError::InvalidMagic);
166        }
167        // There's an extra bit flipped on for whatever reason
168        // We get rid of it here
169        let version = cursor.read_u16::<LittleEndian>()? ^ 0b1_0000_0000;
170        if version > 5 {
171            return Err(ReadError::UnsupportedVersion(version));
172        }
173        let mut tilemap = TileMap::default();
174        let mut global_dimensions = (16, 16);
175        loop {
176            let mut block_id = [0; 4];
177            if let Err(err) = cursor.read_exact(&mut block_id) {
178                if matches!(err.kind(), io::ErrorKind::UnexpectedEof) {
179                    // Reached EOF, stop
180                    break;
181                }
182                // Other IO error, raise it
183                return Err(ReadError::IoError(err));
184            }
185            // Block size is of no use to us
186            let _block_size = cursor.read_u32::<LittleEndian>()?;
187            match &block_id {
188                b"MAP " => {
189                    // Mapping of strings to arbitrary data
190                    if version >= 3 {
191                        let count = cursor.read_u16::<LittleEndian>()?;
192                        for _ in 0..count {
193                            let name = read_helper::read_short_string(&mut cursor)?;
194                            let ty = cursor.read_u8()?;
195                            let property = match ty {
196                                // Integer
197                                0 => Property::Integer(cursor.read_i32::<LittleEndian>()?),
198                                1 => Property::Float(cursor.read_f32::<LittleEndian>()?),
199                                2 => Property::String(read_helper::read_long_string(&mut cursor)?),
200                                t => return Err(ReadError::InvalidType(t)),
201                            };
202                            let _ = tilemap
203                                .properties
204                                .insert(String::from_utf8_lossy(&name).into_owned(), property);
205                        }
206                    } else {
207                        // Deprecated, only in older versions
208                        global_dimensions = (
209                            cursor.read_u16::<LittleEndian>()?,
210                            cursor.read_u16::<LittleEndian>()?,
211                        );
212                    }
213                }
214                b"TILE" => {
215                    let amount = cursor.read_u8()?;
216                    for _ in 0..amount {
217                        // Color is stored in xBGR
218                        let mut buf = [0; 4];
219                        cursor.read_exact(&mut buf)?;
220                        let raw_path = read_helper::read_short_string(&mut cursor)?;
221                        tilemap.tilesets.push(TileSet {
222                            path: String::from_utf8_lossy(&raw_path).into_owned(),
223                            transparent_color: (buf[3], buf[2], buf[1]),
224                        });
225                    }
226                }
227                b"LAYR" => {
228                    let amount = if version == 0 {
229                        cursor.read_u8()? as u16
230                    } else {
231                        cursor.read_u16::<LittleEndian>()?
232                    };
233                    for _ in 0..amount {
234                        let mut layer = Layer::default();
235                        let (width, height) = (
236                            cursor.read_u32::<LittleEndian>()?,
237                            cursor.read_u32::<LittleEndian>()?,
238                        );
239                        layer.width = width;
240                        layer.height = height;
241                        layer.tile_dimensions = if version >= 2 {
242                            (
243                                cursor.read_u16::<LittleEndian>()?,
244                                cursor.read_u16::<LittleEndian>()?,
245                            )
246                        } else {
247                            // Read global dimensions
248                            global_dimensions
249                        };
250                        // Python struct syntax: =2B2i2f3?f
251                        (
252                            layer.tileset,
253                            layer.collision,
254                            layer.offset,
255                            layer.scroll,
256                            layer.wrap,
257                            layer.visible,
258                            layer.opacity,
259                        ) = (
260                            cursor.read_u8()?,
261                            cursor.read_u8()?,
262                            (
263                                cursor.read_i32::<LittleEndian>()?,
264                                cursor.read_i32::<LittleEndian>()?,
265                            ),
266                            (
267                                cursor.read_f32::<LittleEndian>()?,
268                                cursor.read_f32::<LittleEndian>()?,
269                            ),
270                            (cursor.read_u8()? > 0, cursor.read_u8()? > 0),
271                            cursor.read_u8()? > 0,
272                            cursor.read_f32::<LittleEndian>()?,
273                        );
274                        // Read sublayer link
275                        if version >= 4 {
276                            layer.sublayer_link.tileset = cursor.read_u8()?;
277                            layer.sublayer_link.animation = cursor.read_u8()?;
278                            if version == 5 {
279                                layer.sublayer_link.animation_frame = cursor.read_u8()?;
280                            }
281                        }
282                        // Read data blocks
283                        let data_count = cursor.read_u8()?;
284                        let mut header_buf = [0; 4];
285                        for _ in 0..data_count {
286                            cursor.read_exact(&mut header_buf)?;
287                            match &header_buf {
288                                b"MAIN" => {
289                                    // Read the tiles
290                                    let raw_tiles = read_helper::read_compressed(&mut cursor)?;
291                                    if raw_tiles.len() % 2 != 0 {
292                                        return Err(ReadError::InvalidLayerLength);
293                                    }
294                                    // We cannot do reinterpretation here,
295                                    // since Tile.id has an alignment of 2,
296                                    // while the vector has an alignment of 1.
297                                    layer.data = raw_tiles
298                                        .into_boxed_slice()
299                                        .chunks(2)
300                                        .map(|chunk| Tile {
301                                            position: if cfg!(target_endian = "big") {
302                                                [chunk[0], chunk[1]]
303                                            } else {
304                                                [chunk[1], chunk[0]]
305                                            },
306                                        })
307                                        .collect();
308                                }
309                                b"DATA" => {
310                                    let cell_size = cursor.read_u8()?.min(4);
311                                    let mut default_value = [0; 4];
312                                    cursor.read_exact(&mut default_value)?;
313                                    let (w, h) = (layer.width, layer.height);
314                                    let sublayer =
315                                        layer.add_sublayer(&default_value[..cell_size as usize]);
316                                    sublayer.resize(w, h);
317                                    let sublayer_data = read_helper::read_compressed(&mut cursor)?;
318                                    if sublayer_data.len()
319                                        != (sublayer.width as usize
320                                            * sublayer.height as usize
321                                            * sublayer.cell_size as usize)
322                                    {
323                                        return Err(ReadError::InvalidLayerLength);
324                                    }
325                                    sublayer.data = sublayer_data;
326                                }
327                                header => {
328                                    let header = String::from_utf8_lossy(header).into_owned();
329                                    return Err(ReadError::InvalidHeader(header));
330                                }
331                            }
332                        }
333                        tilemap.layers.push(layer);
334                    }
335                }
336                header => {
337                    let header = String::from_utf8_lossy(header).into_owned();
338                    return Err(ReadError::InvalidHeader(header));
339                }
340            }
341        }
342        Ok(tilemap)
343    }
344
345    /// Attempts to write a tilemap to a writable.
346    ///
347    /// # Errors
348    /// The file failed to be written.
349    pub fn write(&self, mut cursor: impl Write) -> Result<(), io::Error> {
350        // Write magic string
351        cursor.write_all(b"ACHTUNG!")?;
352        // Always write version 5
353        // The version has an extra bit
354        cursor.write_u8(5)?;
355        cursor.write_u8(1)?;
356        if !self.properties.is_empty() {
357            let mut cur = Header::new(&mut cursor, b"MAP ");
358            // Can only store up to 65535 properties
359            cur.write_u16::<LittleEndian>(self.properties.len().min(u16::MAX as usize) as u16)?;
360            for (key, value) in self.properties.iter().take(0xFFFF) {
361                write_helper::write_short_string(&mut cur, key)?;
362                match value {
363                    Property::Integer(i) => {
364                        cur.write_u8(0)?; // Integer: 0
365                        cur.write_i32::<LittleEndian>(*i)?;
366                    }
367                    Property::Float(f) => {
368                        cur.write_u8(1)?; // Float: 1
369                        cur.write_f32::<LittleEndian>(*f)?;
370                    }
371                    Property::String(s) => {
372                        cur.write_u8(2)?; // String: 2
373                        write_helper::write_long_string(&mut cur, s)?;
374                    }
375                }
376            }
377            cur.write_header()?;
378        }
379        if !self.tilesets.is_empty() {
380            let mut cur = Header::new(&mut cursor, b"TILE");
381            let len = self.tilesets.len().min(255) as u8;
382            cur.write_u8(len)?;
383            for tileset in self.tilesets.iter().take(0xFF) {
384                cur.write_u8(0)?; // Padding
385                cur.write_u8(tileset.transparent_color.2)?; // B
386                cur.write_u8(tileset.transparent_color.1)?; // G
387                cur.write_u8(tileset.transparent_color.0)?; // R
388                write_helper::write_short_string(&mut cur, &tileset.path)?;
389            }
390            cur.write_header()?;
391        }
392        if !self.layers.is_empty() {
393            let mut cur = Header::new(&mut cursor, b"LAYR");
394            // Can only store up to 65535 layers
395            cur.write_u16::<LittleEndian>(self.layers.len().min(u16::MAX as usize) as u16)?;
396            for layer in self.layers.iter().take(0xFFFF) {
397                cur.write_u32::<LittleEndian>(layer.width)?;
398                cur.write_u32::<LittleEndian>(layer.height)?;
399                // Write layer settings
400                cur.write_u16::<LittleEndian>(layer.tile_dimensions.0)?;
401                cur.write_u16::<LittleEndian>(layer.tile_dimensions.1)?;
402                cur.write_u8(layer.tileset)?;
403                cur.write_u8(layer.collision)?;
404                cur.write_i32::<LittleEndian>(layer.offset.0)?;
405                cur.write_i32::<LittleEndian>(layer.offset.1)?;
406                cur.write_f32::<LittleEndian>(layer.scroll.0)?;
407                cur.write_f32::<LittleEndian>(layer.scroll.1)?;
408                cur.write_u8(layer.wrap.0 as u8)?;
409                cur.write_u8(layer.wrap.1 as u8)?;
410                cur.write_u8(layer.visible as u8)?;
411                cur.write_f32::<LittleEndian>(layer.opacity)?;
412                // Write sublayer link
413                cur.write_u8(layer.sublayer_link.tileset)?;
414                cur.write_u8(layer.sublayer_link.animation)?;
415                cur.write_u8(layer.sublayer_link.animation_frame)?;
416                if layer.width.min(layer.height) == 0 {
417                    // Empty layer
418                    cur.write_u8(0)?; // Layer size
419                    continue;
420                }
421                // Number of headers in this section
422                // Add one for the main header
423                cur.write_u8((layer.sublayers.len() + 1).min(255) as u8)?;
424                cur.write_all(b"MAIN")?;
425                // Use bytemuck to safely cast the tiles
426                let raw_tiles = layer.data.as_slice();
427                let byte_slice: &[u8] = cast_slice(raw_tiles);
428                write_helper::write_compressed(&mut cur, byte_slice)?;
429                for sublayer in layer.sublayers.iter().take(255) {
430                    cur.write_all(b"DATA")?;
431                    cur.write_u8(sublayer.cell_size)?;
432                    cur.write_all(&sublayer.default_value)?;
433                    write_helper::write_compressed(&mut cur, sublayer.data.as_slice())?;
434                }
435            }
436            cur.write_header()?;
437        }
438        Ok(())
439    }
440
441    /// Constructs a new instance from the default.
442    #[inline]
443    #[must_use]
444    pub fn new() -> Self {
445        Self::default()
446    }
447}
448
449/// A single layer of a tilemap.
450#[derive(Clone, PartialEq)]
451pub struct Layer {
452    pub(crate) data: Vec<Tile>,
453    /// Width of this layer.
454    pub(crate) width: u32,
455    /// Height of this layer.
456    pub(crate) height: u32,
457    /// Index of the tileset of this layer.
458    pub tileset: u8,
459    /// Index of the collision of this layer.
460    pub collision: u8,
461    /// The XY position offset of this layer.
462    pub offset: (i32, i32),
463    /// The XY scroll of this layer.
464    pub scroll: (f32, f32),
465    /// Which axes among XY this layer wraps on.
466    pub wrap: (bool, bool),
467    /// Whether the layer is visible.
468    pub visible: bool,
469    /// Opacity of this layer.
470    pub opacity: f32,
471    /// Dimensions of the tiles in this layer.
472    pub tile_dimensions: (u16, u16),
473    /// The sublayers of this layer.
474    /// Any more than 255 sublayers will not be saved.
475    pub sublayers: Vec<SubLayer>,
476    /// The sublayer link of this layer.
477    pub sublayer_link: SubLayerLink,
478}
479
480impl IntoIterator for Layer {
481    type Item = Tile;
482    type IntoIter = std::vec::IntoIter<Tile>;
483
484    fn into_iter(self) -> Self::IntoIter {
485        self.data.into_iter()
486    }
487}
488
489impl Default for Layer {
490    fn default() -> Self {
491        Layer {
492            data: Vec::new(),
493            width: 0,
494            height: 0,
495            tileset: 0,
496            collision: 0,
497            offset: (0, 0),
498            scroll: (0.0, 0.0),
499            wrap: (false, false),
500            visible: true,
501            opacity: 1.0,
502            tile_dimensions: (16, 16),
503            sublayer_link: SubLayerLink::default(),
504            sublayers: Vec::new(),
505        }
506    }
507}
508
509impl Layer {
510    /// Resize the layer, filling empty tiles with the tile default (`0xFFFF`).
511    ///
512    /// If the width is changed, this will reallocate the data buffer!
513    pub fn resize(&mut self, width: u32, height: u32) {
514        if (self.width == width && self.height == height)
515            || ((self.width == 0 || self.height == 0) && (width == 0 || height == 0))
516        {
517            // This does nothing!
518            return;
519        }
520        if width == 0 || height == 0 {
521            // Clear
522            self.width = 0;
523            self.height = 0;
524            self.data.clear();
525            for sublayer in &mut self.sublayers {
526                sublayer.resize(width, height);
527            }
528            return;
529        }
530        if self.width == 0 || self.height == 0 {
531            // Construct
532            self.width = width;
533            self.height = height;
534            self.data = iter::repeat(Tile::default())
535                .take((width * height) as usize)
536                .collect();
537            for sublayer in &mut self.sublayers {
538                sublayer.resize(width, height);
539            }
540            return;
541        }
542        if self.height > height {
543            // Remove rows
544            self.data.truncate((self.width * height) as usize);
545        } else if self.height < height {
546            // Add rows
547            self.data.extend(
548                iter::repeat(Tile::default()).take((self.width * (height - self.height)) as usize),
549            );
550        }
551        if self.width != width {
552            let chunks = self.data.chunks(self.width as usize);
553            self.data = if self.width < width {
554                // Old less than new, add elements
555                chunks
556                    .flat_map(|chunk| {
557                        chunk.iter().copied().chain(
558                            iter::repeat(Tile::default()).take((width - self.width) as usize),
559                        )
560                    })
561                    .collect()
562            } else {
563                // Truncate elements
564                chunks
565                    .flat_map(|chunk| chunk.iter().copied().take(width as usize))
566                    .collect()
567            };
568        }
569        self.width = width;
570        self.height = height;
571        for sublayer in &mut self.sublayers {
572            sublayer.resize(width, height);
573        }
574    }
575
576    /// Add a new sublayer to the layer, returning a mutable reference to it.
577    pub fn add_sublayer(&mut self, default_value: &[u8]) -> &mut SubLayer {
578        let mut sublayer = SubLayer::default();
579        sublayer.set_default(default_value);
580        sublayer.resize(self.width, self.height);
581        self.sublayers.push(sublayer);
582        // SAFETY: we literally just pushed to this
583        unsafe { self.sublayers.last_mut().unwrap_unchecked() }
584    }
585
586    /// Returns the width of the layer.
587    #[inline]
588    #[must_use]
589    pub fn width(&self) -> u32 {
590        self.width
591    }
592
593    /// Returns the height of the layer.
594    #[inline]
595    #[must_use]
596    pub fn height(&self) -> u32 {
597        self.height
598    }
599
600    /// Get a tile by position.
601    /// Returns None if out of bounds
602    #[must_use]
603    pub fn get(&self, (x, y): (usize, usize)) -> Option<&Tile> {
604        let index = y * self.width as usize + x;
605        self.data.get(index)
606    }
607
608    /// Get a tile by position, mutably.
609    /// Returns None if out of bounds
610    pub fn get_mut(&mut self, (x, y): (usize, usize)) -> Option<&mut Tile> {
611        let index = y * self.width as usize + x;
612        self.data.get_mut(index)
613    }
614
615    /// Constructs a new instance from the default.
616    #[inline]
617    #[must_use]
618    pub fn new() -> Self {
619        Self::default()
620    }
621
622    /// Creates an iterator over each tile of the layer, returning a reference.
623    pub fn iter(&self) -> impl Iterator<Item = &Tile> {
624        self.data.iter()
625    }
626
627    /// Creates an iterator over each tile of the layer, returning a mutable reference.
628    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Tile> {
629        self.data.iter_mut()
630    }
631}
632
633impl Index<(usize, usize)> for Layer {
634    type Output = Tile;
635
636    /// Index by position and return a reference.
637    ///
638    /// # Panics
639    /// Panics if index is out of bounds.
640    fn index(&self, (x, y): (usize, usize)) -> &Self::Output {
641        let index = y * self.width as usize + x;
642        &self.data[index]
643    }
644}
645
646impl IndexMut<(usize, usize)> for Layer {
647    /// Index by position and return a mutable reference.
648    ///
649    /// # Panics
650    /// Panics if index is out of bounds.
651    fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Self::Output {
652        let index = y * self.width as usize + x;
653        &mut self.data[index]
654    }
655}
656
657/// A tileset in the image.
658#[derive(Default, Clone, PartialEq, Eq)]
659pub struct TileSet {
660    /// Path to the tileset image.
661    pub path: String,
662    /// Color treated as transparent.
663    pub transparent_color: (u8, u8, u8),
664}
665
666impl TileSet {
667    /// Constructs a new instance from the default.
668    #[inline]
669    #[must_use]
670    pub fn new() -> Self {
671        Self::default()
672    }
673}
674
675#[derive(Clone, PartialEq)]
676/// A value of a property in a layer of a tilemap.
677pub enum Property {
678    /// Integer.
679    Integer(i32),
680    /// Floating point.
681    Float(f32),
682    /// Arbitrary bytes.
683    /// Trying to write a string with length 0 to a file will fail!
684    String(Vec<u8>),
685}
686
687impl From<i32> for Property {
688    fn from(value: i32) -> Self {
689        Self::Integer(value)
690    }
691}
692
693impl From<f32> for Property {
694    fn from(value: f32) -> Self {
695        Self::Float(value)
696    }
697}
698
699impl From<Vec<u8>> for Property {
700    fn from(value: Vec<u8>) -> Self {
701        Self::String(value)
702    }
703}
704
705impl From<String> for Property {
706    fn from(value: String) -> Self {
707        Self::String(value.into_bytes())
708    }
709}
710
711#[derive(Copy, Clone)]
712#[repr(C)]
713/// A union representing a tile in a tilemap.
714///
715/// The ID should always be stored using big endian.
716/// If it is not, X and Y will be swapped, which is undesirable.
717/// Be careful!
718pub union Tile {
719    /// Identifier
720    pub id: u16,
721    /// XY position
722    pub position: [u8; 2],
723}
724
725// SAFETY: These both hold for both fields.
726unsafe impl Zeroable for Tile {}
727unsafe impl Pod for Tile {}
728
729impl Tile {
730    /// Safely returns the tile's ID.
731    // SAFETY: The size and alignment of u16
732    // is the same as the struct.
733    #[must_use]
734    pub fn id(&self) -> u16 {
735        unsafe { self.id }
736    }
737    /// Safely returns a mutable reference to the tile's ID.
738    ///
739    /// Make sure any writes to this are big endian!
740    /// Little endian won't be unsound, but will swap X and Y
741    /// when reading position.
742    // SAFETY: See above.
743    #[must_use]
744    pub fn id_mut(&mut self) -> &mut u16 {
745        unsafe { &mut self.id }
746    }
747    /// Safely returns the tile's position.
748    // SAFETY: The size of [u8; 2]
749    // is the same as u16, and [u8; 2]
750    // is not #repr(Rust).
751    #[must_use]
752    pub fn position(&self) -> [u8; 2] {
753        unsafe { self.position }
754    }
755    /// Safely returns a mutable reference to the tile's position.
756    // SAFETY: See above.
757    #[must_use]
758    pub fn position_mut(&mut self) -> &mut [u8; 2] {
759        unsafe { &mut self.position }
760    }
761}
762
763impl Default for Tile {
764    fn default() -> Self {
765        Self { id: 0xFFFF }
766    }
767}
768
769impl PartialEq for Tile {
770    fn eq(&self, other: &Self) -> bool {
771        // SAFETY: All fields are of the same type,
772        // and all bit patterns are valid for said fields.
773        unsafe { self.id == other.id }
774    }
775}
776
777/// A sublayer within a layer of a tilemap.
778#[derive(Clone, PartialEq, Eq, Default)]
779pub struct SubLayer {
780    pub(crate) data: Vec<u8>,
781    default_value: [u8; 4],
782    cell_size: u8,
783    width: u32,
784    height: u32,
785}
786
787impl SubLayer {
788    /// Resize the sublayer, filling empty tiles with the sublayer's default value.
789    ///
790    /// If the width is changed, this will reallocate the data buffer!
791    ///
792    /// # Sanity
793    /// The layer this is put into should be the same size as the new size.
794    ///
795    /// # Panics
796    /// Panics if the resulting area overflows a u32.
797    pub fn resize(&mut self, width: u32, height: u32) {
798        if (self.width == width && self.height == height)
799            || ((self.width == 0 || self.height == 0) && (width == 0 || height == 0))
800        {
801            // This does nothing!
802            return;
803        }
804        if width == 0 || height == 0 {
805            // Clear
806            self.width = 0;
807            self.height = 0;
808            self.data.clear();
809            return;
810        }
811        let default = &self.default_value[..self.cell_size as usize];
812        if self.width == 0 || self.height == 0 {
813            // Construct
814            self.width = width;
815            self.height = height;
816            self.data = iter::repeat(default)
817                .take((width * height) as usize)
818                .flatten()
819                .copied()
820                .collect();
821            return;
822        }
823        if self.height > height {
824            // Remove rows
825            self.data
826                .truncate((self.width * height * self.cell_size as u32) as usize);
827        } else if self.height < height {
828            // Add rows
829            self.data.extend(
830                iter::repeat(default)
831                    .take((self.width * (height - self.height)) as usize)
832                    .flatten(),
833            );
834        }
835        if self.width != width {
836            let chunks = self
837                .data
838                .chunks(self.width as usize * self.cell_size as usize);
839            self.data = if self.width < width {
840                // Old less than new, add elements
841                chunks
842                    .flat_map(|chunk| {
843                        chunk.iter().copied().chain(
844                            iter::repeat(default)
845                                .take((width - self.width) as usize)
846                                .flatten()
847                                .copied(),
848                        )
849                    })
850                    .collect()
851            } else {
852                // Truncate elements
853                chunks
854                    .flat_map(|chunk| chunk.iter().take(self.cell_size as usize * width as usize))
855                    .copied()
856                    .collect()
857            };
858        }
859        self.width = width;
860        self.height = height;
861    }
862
863    /// Returns the size of one data cell.
864    #[inline]
865    #[must_use]
866    pub fn cell_size(&self) -> u8 {
867        self.cell_size
868    }
869
870    /// Returns the width of the sublayer.
871    #[inline]
872    #[must_use]
873    pub fn width(&self) -> u32 {
874        self.width
875    }
876
877    /// Returns the height of the sublayer.
878    #[inline]
879    #[must_use]
880    pub fn height(&self) -> u32 {
881        self.height
882    }
883
884    /// Set the default value of the sublayer, resizing all cells to its length.
885    ///
886    /// The default value is truncated to 4 bytes if larger.
887    ///
888    /// If the new default value is larger than the old one, all cells are zero padded to the new length.
889    ///
890    /// If the new default is smaller, all cells are truncated to its length.
891    ///
892    /// This will *only* not reallocate if the length of the new default is the same as the old one!
893    pub fn set_default(&mut self, default: &[u8]) {
894        let old_size = self.cell_size as usize;
895        let new_size = default.len().min(4);
896        let default = default.to_vec();
897        let mut spaced_default = default.clone();
898        spaced_default.resize(4, 0);
899        // SAFETY: due to resizing, this is always exactly 4 bytes long
900        let spaced_default_slice: &[u8; 4] =
901            unsafe { spaced_default.as_slice().try_into().unwrap_unchecked() };
902        self.default_value = *spaced_default_slice;
903        self.cell_size = new_size as u8;
904        if new_size == old_size || self.width == 0 || self.height == 0 {
905            // No need to resize the cells
906            return;
907        }
908        if new_size == 0 {
909            // Cell size is zero
910            self.data = Vec::new();
911            return;
912        }
913        if old_size == 0 {
914            // Need to construct
915            self.data.resize(
916                (self.width * self.height * self.cell_size as u32) as usize,
917                0,
918            );
919            return;
920        }
921        // Resize each cell of the sublayer to the value's size
922        self.data = if new_size > old_size {
923            self.data
924                .as_slice()
925                .chunks(old_size)
926                .flat_map(|cell| {
927                    // Need to 0-pad
928                    cell.iter()
929                        .chain(iter::repeat(&0).take(new_size - old_size))
930                })
931                .copied()
932                .collect()
933        } else {
934            self.data
935                .as_slice()
936                .chunks(old_size)
937                .flat_map(|cell| {
938                    // Need to 0-pad
939                    cell.iter().take(new_size)
940                })
941                .copied()
942                .collect()
943        };
944    }
945
946    /// Get a cell by position.
947    /// Returns None if out of bounds.
948    #[must_use]
949    pub fn get(&self, (x, y): (u32, u32)) -> Option<&[u8]> {
950        if x >= self.width || y >= self.height {
951            return None;
952        }
953        let size = self.cell_size as usize;
954        let start = (y * self.width + x) as usize * size;
955        let end = start + size;
956        Some(&self.data[start..end])
957    }
958
959    /// Get a cell by position, mutably.
960    /// Returns None if out of bounds
961    pub fn get_mut(&mut self, (x, y): (u32, u32)) -> Option<&mut [u8]> {
962        if x >= self.width || y >= self.height {
963            return None;
964        }
965        let size = self.cell_size as usize;
966        let start = (y * self.width + x) as usize * size;
967        let end = start + size;
968        Some(&mut self.data[start..end])
969    }
970
971    /// Constructs a new instance from the default.
972    #[inline]
973    #[must_use]
974    pub fn new() -> Self {
975        Self::default()
976    }
977
978    /// Creates an iterator over each cell of the sublayer, returning a slice.
979    pub fn iter(&self) -> impl Iterator<Item = &[u8]> {
980        self.data.chunks(self.cell_size as usize)
981    }
982
983    /// Creates an iterator over each cell of the sublayer, returning a mutable slice.
984    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut [u8]> {
985        self.data.chunks_mut(self.cell_size as usize)
986    }
987}
988
989impl Index<(u32, u32)> for SubLayer {
990    type Output = [u8];
991
992    /// Index by position and return a reference.
993    ///
994    /// # Panics
995    /// Panics if index is out of bounds.
996    fn index(&self, (x, y): (u32, u32)) -> &Self::Output {
997        let size = self.cell_size as usize;
998        let start = (y * self.width + x) as usize * size;
999        let end = start + size;
1000        &self.data[start..end]
1001    }
1002}
1003
1004impl IndexMut<(u32, u32)> for SubLayer {
1005    /// Index by position and return a mutable reference.
1006    ///
1007    /// # Panics
1008    /// Panics if index is out of bounds.
1009    fn index_mut(&mut self, (x, y): (u32, u32)) -> &mut Self::Output {
1010        let size = self.cell_size as usize;
1011        let start = (y * self.width + x) as usize * size;
1012        let end = start + size;
1013        &mut self.data[start..end]
1014    }
1015}
1016
1017/// A link to a sublayer within a layer.
1018#[derive(Debug, Clone, PartialEq, Eq)]
1019pub struct SubLayerLink {
1020    /// Which sublayer is this layer's tileset linked to?
1021    pub tileset: u8,
1022    /// Which sublayer is this layer's animation linked to?
1023    pub animation: u8,
1024    /// Which sublayer is this layer's animation frames linked to?
1025    pub animation_frame: u8,
1026}
1027
1028impl SubLayerLink {
1029    /// Constructs a new instance from the default.
1030    #[inline]
1031    #[must_use]
1032    pub fn new() -> Self {
1033        Self::default()
1034    }
1035}
1036
1037impl Default for SubLayerLink {
1038    fn default() -> Self {
1039        Self {
1040            tileset: 0xFF,
1041            animation: 0xFF,
1042            animation_frame: 0xFF,
1043        }
1044    }
1045}