1use std::io::{Read, Seek, SeekFrom};
2
3use byteorder::{BigEndian, ReadBytesExt};
4
5use crate::data::error::Error;
6use crate::data::pixeldata::PixelData;
7use crate::data::rgba::RgbaPixel;
8use crate::data::version::Version;
9use crate::TileCursor;
10
11pub trait ParseVersion {
12 fn parse<R: Read>(rdr: R) -> Result<Self, Error>
13 where
14 Self: Sized;
15}
16
17impl ParseVersion for Version {
18 fn parse<R: Read>(mut rdr: R) -> Result<Self, Error> {
19 let mut v = [0; 4];
20 rdr.read_exact(&mut v)?;
21 match &v {
22 b"file" => Ok(Self(0)),
23 [b'v', ver @ ..] => Ok(Self(
24 String::from_utf8_lossy(ver)
25 .parse()
26 .map_err(|_| Error::UnknownVersion)?,
27 )),
28 _ => Err(Error::UnknownVersion),
29 }
30 }
31}
32
33impl PixelData {
34 pub fn parse_hierarchy<R: Read + Seek>(
37 mut rdr: R,
38 version: Version,
39 ) -> Result<PixelData, Error> {
40 let width = rdr.read_u32::<BigEndian>()?;
42 let height = rdr.read_u32::<BigEndian>()?;
43 let bpp = rdr.read_u32::<BigEndian>()?;
44 let lptr = rdr.read_uint::<BigEndian>(version.bytes_per_offset())?;
50 let _dummpy_ptr_pos = rdr.stream_position()?;
51 rdr.seek(SeekFrom::Start(lptr))?;
52 let level_width = rdr.read_u32::<BigEndian>()?;
54 let level_height = rdr.read_u32::<BigEndian>()?;
55 if level_width != width || level_height != height {
56 return Err(Error::InvalidFormat);
57 }
58
59 let mut pixels = vec![RgbaPixel([0, 0, 0, 255]); (width * height) as usize];
60 let mut next_tptr_pos;
61
62 let tiles_x = (f64::from(width) / 64.0).ceil() as u32;
63 let tiles_y = (f64::from(height) / 64.0).ceil() as u32;
64 for ty in 0..tiles_y {
65 for tx in 0..tiles_x {
66 let tptr = rdr.read_uint::<BigEndian>(version.bytes_per_offset())?;
67 next_tptr_pos = rdr.stream_position()?;
68 rdr.seek(SeekFrom::Start(tptr))?;
69
70 let mut cursor = TileCursor::new(width, height, tx, ty, bpp);
71 cursor.feed(&mut rdr, &mut pixels)?;
72
73 rdr.seek(SeekFrom::Start(next_tptr_pos))?;
74 }
75 }
76
77 Ok(PixelData {
89 pixels,
90 width,
91 height,
92 })
93 }
94
95 pub fn pixel(&self, x: u32, y: u32) -> Option<RgbaPixel> {
96 if x >= self.width || y >= self.height {
97 return None;
98 }
99 Some(self.pixels[(y * self.width + x) as usize])
100 }
101
102 pub fn raw_sub_rgba_buffer(&self, x: u32, y: u32, width: u32, height: u32) -> Vec<u8> {
108 let mut sub = Vec::with_capacity((width * height * 4) as usize);
109 for _y in y..(y + height) {
110 for _x in x..(x + width) {
111 if _y > self.height || _x > self.width {
112 panic!("Pixel access is out of bounds");
113 }
114 sub.extend_from_slice(&self.pixel(_x, _y).unwrap().0);
115 }
116 }
117 sub
118 }
119}