1use std::collections::HashMap;
2use std::fmt::{Display, Formatter};
3use std::num::NonZeroUsize;
4
5use thiserror::Error;
6use crate::BlendSource;
7
8use crate::parse::input::Input;
9use crate::parse::parsers::parse_blend;
10
11mod parsers;
12mod input;
13
14pub type Location = usize;
15
16#[derive(Debug)]
17pub struct BlendFile {
18 pub header: FileHeader,
19 pub blocks: Vec<FileBlock>,
20 pub dna: Dna,
21 address_table: AddressTable,
22}
23
24impl BlendFile {
25
26 pub fn look_up<T>(&self, address: T) -> Option<&FileBlock>
27 where T: AddressLike {
28 self.address_table
29 .get(&address.address())
30 .map(|index| self.blocks.get(*index))
31 .flatten()
32 }
33}
34
35pub type Address = NonZeroUsize;
36pub type AddressTable = HashMap<Address, usize>;
37
38pub trait AddressLike {
39 fn address(&self) -> Address;
40}
41
42impl AddressLike for NonZeroUsize {
43 fn address(&self) -> Address {
44 *self
45 }
46}
47
48impl AddressLike for &NonZeroUsize {
49 fn address(&self) -> Address {
50 **self
51 }
52}
53
54#[derive(Error, Debug)]
55pub enum BlendParseError {
56
57 #[error("Failed to parse header!")]
58 ParseHeaderError,
59
60 #[error("An error of kind '{kind}' occurred while parsing the dna!")]
61 ParseDnaError { kind: String },
62
63 #[error("Failed to parse dna, due to missing input!")]
64 IncompleteDnaError,
65
66 #[error("An error occurred parsing blend file!")]
67 ParseError,
68}
69
70#[derive(Debug)]
71pub struct Dna {
72 pub field_names: Vec<String>,
73 pub types: Vec<DnaType>,
74 pub structs: Vec<DnaStruct>,
75 pub pointer_size: usize,
76}
77
78impl Dna {
79
80 pub fn find_field_name_of(&self, field: &DnaField) -> Option<&String> {
81 self.field_names.get(field.name_index)
82 }
83
84 pub fn find_type_of<A>(&self, typed: A) -> Option<&DnaType>
85 where A: HasDnaTypeIndex {
86 self.types.get(typed.type_index(self))
87 }
88
89 pub fn find_struct_of(&self, block: &FileBlock) -> Option<&DnaStruct> {
90 self.structs.get(block.sdna)
91 }
92
93 pub fn find_struct_by_name(&self, name: &str) -> Option<(usize, &DnaStruct)> {
94 self.structs.iter().enumerate().find(|(_index, dna_struct)| {
95 self.find_type_of(*dna_struct)
96 .map(|dna_type| name == dna_type.name)
97 .unwrap_or(false)
98 })
99 }
100}
101
102#[derive(Debug, PartialEq)]
103pub struct DnaType {
104 pub name: String,
105 pub size: usize,
106}
107
108impl DnaType {
109
110 pub fn new(name: &'static str, size: usize) -> DnaType {
111 DnaType {
112 name: String::from(name),
113 size
114 }
115 }
116}
117
118#[derive(Debug)]
119pub struct DnaStruct {
120 pub type_index: usize,
121 pub fields: Vec<DnaField>,
122}
123
124#[derive(Debug)]
125pub struct DnaField {
126 pub name_index: usize,
127 pub type_index: usize,
128}
129
130pub trait HasDnaTypeIndex {
131 fn type_index(&self, dna: &Dna) -> usize;
132}
133
134impl HasDnaTypeIndex for &DnaField {
135 fn type_index(&self, _: &Dna) -> usize {
136 self.type_index
137 }
138}
139
140impl HasDnaTypeIndex for &DnaStruct {
141 fn type_index(&self, _: &Dna) -> usize {
142 self.type_index
143 }
144}
145
146impl HasDnaTypeIndex for &FileBlock {
147 fn type_index(&self, dna: &Dna) -> usize {
148 dna.find_struct_of(self)
149 .expect("Could not determine struct of FileBlock!")
150 .type_index
151 }
152}
153
154impl HasDnaTypeIndex for usize {
155 fn type_index(&self, _: &Dna) -> usize {
156 *self
157 }
158}
159
160#[derive(Debug, Copy, Clone, PartialEq)]
161pub struct FileHeader {
162 pub pointer_size: PointerSize,
163 pub endianness: Endianness,
164 pub version: Version,
165}
166
167#[derive(Debug, Copy, Clone, PartialEq)]
168pub enum PointerSize {
169 Pointer4Bytes,
170 Pointer8Bytes
171}
172
173impl PointerSize {
174 pub fn size(&self) -> usize {
175 match self {
176 PointerSize::Pointer4Bytes => 4,
177 PointerSize::Pointer8Bytes => 8,
178 }
179 }
180}
181
182#[derive(Debug, Copy, Clone, PartialEq)]
183pub enum Endianness {
184 Little,
185 Big
186}
187
188impl Display for Endianness {
189 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
190 let value = match self {
191 Endianness::Little => "Little",
192 Endianness::Big => "Big"
193 };
194 write!(formatter, "{}", value)
195 }
196}
197
198#[derive(Debug, Copy, Clone, PartialEq)]
199pub struct Version {
200 pub major: char,
201 pub minor: char,
202 pub patch: char,
203}
204
205impl Version {
206 pub const fn new(major: char, minor: char, patch: char) -> Version {
207 Version { major, minor, patch }
208 }
209}
210
211impl Display for Version {
212 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
213 write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
214 }
215}
216
217#[derive(Debug, Copy, Clone)]
218pub struct FileBlock {
219 pub identifier: Identifier,
220 pub length: usize,
221 pub address: Option<NonZeroUsize>,
222 pub sdna: usize,
223 pub count: usize,
224 block_location: Location,
225 data_location: Location,
226}
227
228impl FileBlock {
229
230 pub fn block_location(&self) -> Location {
231 self.block_location
232 }
233
234 pub fn data_location(&self) -> Location {
235 self.data_location
236 }
237}
238
239#[derive(Debug, Hash, Copy, Clone, Eq, PartialEq)]
240pub enum Identifier {
241 Unknown { code: [u8; 4] },
242 REND,
244 TEST,
245 GLOB,
246 DATA,
247 WM,
248 IM,
249 SN,
250 WS,
251 BR,
252 SC,
254 PL,
255 OB,
257 GR,
258 CA,
259 LA,
260 ME,
262 WO,
263 LS,
264 MA,
266 DNA,
268 ENDB,
270}
271
272impl Display for Identifier {
273 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
274 let text = match self {
275 Identifier::Unknown { .. } => "Unknown",
276 Identifier::REND => "REND",
277 Identifier::TEST => "Test",
278 Identifier::GLOB => "Glob",
279 Identifier::DATA => "Data",
280 Identifier::WM => "WM",
281 Identifier::IM => "IM",
282 Identifier::SN => "SN",
283 Identifier::WS => "WS",
284 Identifier::BR => "BR",
285 Identifier::SC => "Scene",
286 Identifier::PL => "PL",
287 Identifier::OB => "Object",
288 Identifier::GR => "BR",
289 Identifier::CA => "CA",
290 Identifier::LA => "LA",
291 Identifier::ME => "Mesh",
292 Identifier::WO => "WO",
293 Identifier::LS => "LS",
294 Identifier::MA => "Material",
295 Identifier::DNA => "DNA",
296 Identifier::ENDB => "ENDB",
297 };
298 write!(formatter, "{}", text)
299 }
300}
301
302pub fn parse<'a, A>(source: A) -> Result<BlendFile, BlendParseError>
303where A: BlendSource<'a> {
304 let input = Input::new(source.data(), None, None);
305 parse_blend(input).map(|(header, blocks, dna)| {
306 let address_table: AddressTable = blocks.iter()
307 .enumerate()
308 .filter_map(|(index, block)| match block.address {
309 None => None,
310 Some(address) => Some((address, index))
311 })
312 .collect();
313 BlendFile {
314 header,
315 blocks,
316 dna,
317 address_table
318 }
319 })
320}