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 )]
9
10use 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#[derive(Clone, PartialEq, Default)]
62pub struct TileMap {
63 pub layers: Vec<Layer>,
66 pub tilesets: Vec<TileSet>,
69 pub properties: HashMap<String, Property>,
72}
73
74pub enum ReadError {
76 IoError(io::Error),
78 InvalidMagic,
80 UnsupportedVersion(u16),
82 InvalidType(u8),
84 InvalidLayerLength,
86 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
120struct 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 pub fn read(mut cursor: impl Read) -> Result<Self, ReadError> {
161 let mut buf = [0; 8];
163 cursor.read_exact(&mut buf)?;
164 if &buf != b"ACHTUNG!" {
165 return Err(ReadError::InvalidMagic);
166 }
167 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 break;
181 }
182 return Err(ReadError::IoError(err));
184 }
185 let _block_size = cursor.read_u32::<LittleEndian>()?;
187 match &block_id {
188 b"MAP " => {
189 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 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 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 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 global_dimensions
249 };
250 (
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 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 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 let raw_tiles = read_helper::read_compressed(&mut cursor)?;
291 if raw_tiles.len() % 2 != 0 {
292 return Err(ReadError::InvalidLayerLength);
293 }
294 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 pub fn write(&self, mut cursor: impl Write) -> Result<(), io::Error> {
350 cursor.write_all(b"ACHTUNG!")?;
352 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 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)?; cur.write_i32::<LittleEndian>(*i)?;
366 }
367 Property::Float(f) => {
368 cur.write_u8(1)?; cur.write_f32::<LittleEndian>(*f)?;
370 }
371 Property::String(s) => {
372 cur.write_u8(2)?; 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)?; cur.write_u8(tileset.transparent_color.2)?; cur.write_u8(tileset.transparent_color.1)?; cur.write_u8(tileset.transparent_color.0)?; 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 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 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 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 cur.write_u8(0)?; continue;
420 }
421 cur.write_u8((layer.sublayers.len() + 1).min(255) as u8)?;
424 cur.write_all(b"MAIN")?;
425 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 #[inline]
443 #[must_use]
444 pub fn new() -> Self {
445 Self::default()
446 }
447}
448
449#[derive(Clone, PartialEq)]
451pub struct Layer {
452 pub(crate) data: Vec<Tile>,
453 pub(crate) width: u32,
455 pub(crate) height: u32,
457 pub tileset: u8,
459 pub collision: u8,
461 pub offset: (i32, i32),
463 pub scroll: (f32, f32),
465 pub wrap: (bool, bool),
467 pub visible: bool,
469 pub opacity: f32,
471 pub tile_dimensions: (u16, u16),
473 pub sublayers: Vec<SubLayer>,
476 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 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 return;
519 }
520 if width == 0 || height == 0 {
521 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 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 self.data.truncate((self.width * height) as usize);
545 } else if self.height < height {
546 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 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 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 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 unsafe { self.sublayers.last_mut().unwrap_unchecked() }
584 }
585
586 #[inline]
588 #[must_use]
589 pub fn width(&self) -> u32 {
590 self.width
591 }
592
593 #[inline]
595 #[must_use]
596 pub fn height(&self) -> u32 {
597 self.height
598 }
599
600 #[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 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 #[inline]
617 #[must_use]
618 pub fn new() -> Self {
619 Self::default()
620 }
621
622 pub fn iter(&self) -> impl Iterator<Item = &Tile> {
624 self.data.iter()
625 }
626
627 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 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 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#[derive(Default, Clone, PartialEq, Eq)]
659pub struct TileSet {
660 pub path: String,
662 pub transparent_color: (u8, u8, u8),
664}
665
666impl TileSet {
667 #[inline]
669 #[must_use]
670 pub fn new() -> Self {
671 Self::default()
672 }
673}
674
675#[derive(Clone, PartialEq)]
676pub enum Property {
678 Integer(i32),
680 Float(f32),
682 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)]
713pub union Tile {
719 pub id: u16,
721 pub position: [u8; 2],
723}
724
725unsafe impl Zeroable for Tile {}
727unsafe impl Pod for Tile {}
728
729impl Tile {
730 #[must_use]
734 pub fn id(&self) -> u16 {
735 unsafe { self.id }
736 }
737 #[must_use]
744 pub fn id_mut(&mut self) -> &mut u16 {
745 unsafe { &mut self.id }
746 }
747 #[must_use]
752 pub fn position(&self) -> [u8; 2] {
753 unsafe { self.position }
754 }
755 #[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 unsafe { self.id == other.id }
774 }
775}
776
777#[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 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 return;
803 }
804 if width == 0 || height == 0 {
805 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 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 self.data
826 .truncate((self.width * height * self.cell_size as u32) as usize);
827 } else if self.height < height {
828 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 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 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 #[inline]
865 #[must_use]
866 pub fn cell_size(&self) -> u8 {
867 self.cell_size
868 }
869
870 #[inline]
872 #[must_use]
873 pub fn width(&self) -> u32 {
874 self.width
875 }
876
877 #[inline]
879 #[must_use]
880 pub fn height(&self) -> u32 {
881 self.height
882 }
883
884 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 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 return;
907 }
908 if new_size == 0 {
909 self.data = Vec::new();
911 return;
912 }
913 if old_size == 0 {
914 self.data.resize(
916 (self.width * self.height * self.cell_size as u32) as usize,
917 0,
918 );
919 return;
920 }
921 self.data = if new_size > old_size {
923 self.data
924 .as_slice()
925 .chunks(old_size)
926 .flat_map(|cell| {
927 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 cell.iter().take(new_size)
940 })
941 .copied()
942 .collect()
943 };
944 }
945
946 #[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 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 #[inline]
973 #[must_use]
974 pub fn new() -> Self {
975 Self::default()
976 }
977
978 pub fn iter(&self) -> impl Iterator<Item = &[u8]> {
980 self.data.chunks(self.cell_size as usize)
981 }
982
983 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 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 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#[derive(Debug, Clone, PartialEq, Eq)]
1019pub struct SubLayerLink {
1020 pub tileset: u8,
1022 pub animation: u8,
1024 pub animation_frame: u8,
1026}
1027
1028impl SubLayerLink {
1029 #[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}