assembly_maps/lvl/
reader.rs

1//! # Low level reading
2use super::file::*;
3use super::parser;
4
5use assembly_core::reader::{FileError, FileResult};
6use assembly_core::{nom::Finish, reader::ParseAt};
7
8use std::io::prelude::*;
9use std::{io::SeekFrom, num::NonZeroU32};
10
11/// A low level reader class
12pub struct LevelReader<T> {
13    inner: T,
14}
15
16impl<T> LevelReader<T> {
17    pub fn new(inner: T) -> Self {
18        Self { inner }
19    }
20}
21
22fn get_offset(header: &FileMetaChunkData, id: u32) -> Option<NonZeroU32> {
23    match id {
24        2000 => NonZeroU32::new(header.chunk_2000_offset),
25        2001 => NonZeroU32::new(header.chunk_2001_offset),
26        2002 => NonZeroU32::new(header.chunk_2002_offset),
27        _ => None,
28    }
29}
30
31impl<T> LevelReader<T>
32where
33    T: Read + Seek,
34{
35    /// Seek to the chunk data
36    pub fn seek_to(&mut self, header: &ChunkHeader) -> FileResult<()> {
37        self.inner.seek(SeekFrom::Start(header.offset.into()))?;
38        Ok(())
39    }
40
41    pub fn load_buf(&mut self, base: u32, header: &ChunkHeader) -> FileResult<Vec<u8>> {
42        self.seek_to(header)?;
43        let len = header.size - (header.offset - base);
44        let mut buf = vec![0; len as usize];
45        self.inner.read_exact(&mut buf[..])?;
46        Ok(buf)
47    }
48
49    /// Seek meta
50    pub fn get_chunk(
51        &mut self,
52        header: &FileMetaChunkData,
53        id: u32,
54    ) -> Option<FileResult<ChunkHeader>> {
55        get_offset(header, id).map(|offset| {
56            self.inner.seek(SeekFrom::Start(u32::from(offset).into()))?;
57            self.get_chunk_header()
58        })
59    }
60
61    /// Load a chunk header
62    pub fn get_chunk_header(&mut self) -> FileResult<ChunkHeader> {
63        let mut header_bytes = [0; 20];
64        self.inner.read_exact(&mut header_bytes)?;
65        let (_rest, header) = parser::parse_chunk_header(&header_bytes)
66            .finish()
67            .at(0xbeef, &header_bytes)?;
68        Ok(header)
69    }
70
71    /// Get the chunk meta data
72    pub fn get_meta_chunk_data(&mut self) -> FileResult<FileMetaChunkData> {
73        let mut meta_chunk_data_bytes = [0 as u8; 20];
74        self.inner.read_exact(&mut meta_chunk_data_bytes)?;
75        let (_rest, meta_chunk_data) = parser::parse_file_meta_chunk_data(&meta_chunk_data_bytes)
76            .finish()
77            .at(0xbeef, &meta_chunk_data_bytes)?;
78        Ok(meta_chunk_data)
79    }
80
81    /// Get the meta chunk
82    pub fn get_meta_chunk(&mut self) -> FileResult<FileMetaChunk> {
83        let header = self.get_chunk_header()?;
84        self.inner.seek(SeekFrom::Start(header.offset.into()))?;
85        let data = self.get_meta_chunk_data()?;
86        Ok(FileMetaChunk { header, data })
87    }
88
89    pub fn read_level_file(&mut self) -> FileResult<Level> {
90        let header_1000 = self.get_chunk_header()?;
91
92        if !header_1000.id == 1000 {
93            return Err(FileError::Custom("Expected first chunk to be of type 1000"));
94        }
95
96        self.seek_to(&header_1000)?;
97        let meta = self.get_meta_chunk_data()?;
98
99        let env = self
100            .get_chunk(&meta, 2000)
101            .map(|res| {
102                let header_2000 = res?;
103
104                if header_2000.id != 2000 {
105                    return Err(FileError::Custom("Expected 2000 chunk to be of type 2000"));
106                }
107
108                let buf = self.load_buf(meta.chunk_2000_offset, &header_2000)?;
109                let env = parser::parse_env_chunk_data(&buf)
110                    .finish()
111                    .at(meta.chunk_2000_offset.into(), &buf)?
112                    .1;
113
114                // first section
115                let sec1_base = (env.section1_address - header_2000.offset) as usize;
116                let sec1 = parser::parse_section1(meta.version, &buf[sec1_base..])
117                    .finish()
118                    .at(env.section1_address.into(), &buf[sec1_base..])?
119                    .1;
120
121                // sky section
122                let sky_base = (env.sky_address - header_2000.offset) as usize;
123                let sky = parser::parse_sky_section(&buf[sky_base..])
124                    .finish()
125                    .at(env.sky_address.into(), &buf[sky_base..])?
126                    .1;
127
128                // TODO: third section
129                Ok(Environment { sec1, sky })
130            })
131            .transpose()?;
132
133        let objects = self
134            .get_chunk(&meta, 2001)
135            .map(|res| {
136                let header_2001 = res?;
137
138                if header_2001.id != 2001 {
139                    return Err(FileError::Custom("Expected 2001 chunk to be of type 2001"));
140                }
141
142                let buf = self.load_buf(meta.chunk_2001_offset, &header_2001)?;
143                let obj = parser::parse_objects_chunk_data(meta.version, &buf)
144                    .finish()
145                    .at(meta.chunk_2001_offset.into(), &buf)?
146                    .1;
147
148                let obj = obj
149                    .parse_settings()
150                    .map_err(|_| FileError::Custom("Failed to parse object settings"))?;
151
152                Ok(obj.objects)
153            })
154            .transpose()?
155            .unwrap_or_default();
156
157        Ok(Level { env, objects })
158    }
159}