assembly_maps/lvl/
reader.rs1use 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
11pub 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 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 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 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 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 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 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 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 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}