blend_inspect_rs/parse/
mod.rs

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    /// Identifier for the end of the file header of a blend file.
243    REND,
244    TEST,
245    GLOB,
246    DATA,
247    WM,
248    IM,
249    SN,
250    WS,
251    BR,
252    /// Identifier for a [`FileBlock`] containing scene information.
253    SC,
254    PL,
255    /// Identifier for a [`FileBlock`] containing object information.
256    OB,
257    GR,
258    CA,
259    LA,
260    /// Identifier for a [`FileBlock`] containing mesh data.
261    ME,
262    WO,
263    LS,
264    /// Identifier for a [`FileBlock`] containing material data.
265    MA,
266    /// Identifier for the DNA block.
267    DNA,
268    /// Identifier for the end of a blend file.
269    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}