1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
use std::io::{self,Read};
use std::fs;
use std::path::{Path, PathBuf};
use std::mem;
use std::str;
use std::collections::HashMap;
use types::Endianness;
use file_block::FileBlock;
use object::Object;
use sdna::SDNA;
use std::io::BufReader;
use std::iter;

#[derive(Debug,Clone)]
#[repr(C)]
pub struct Header{
    identifier: [u8; 7],
    pointer_size: u8,
    endianness: u8,
    version_number: [u8; 3],
}

impl Header{
    fn parse<R: Read>(file: &mut R) -> io::Result<Header>{
        let mut header = [0u8; 12];
        file.read_exact(&mut header)?;
        let header: Header = unsafe{
            mem::transmute(header)
        };

        let identifier = str::from_utf8(&header.identifier)
            .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
        if identifier == "BLENDER" {
            Ok(header)
        }else{
            Err(io::Error::new(io::ErrorKind::InvalidData, "File header not 'BLENDER'"))
        }
    }

    pub fn endianness(&self) -> Result<Endianness,()>{
        match self.endianness as char{
            'v' => Ok(Endianness::LittleEndian),
            'V' => Ok(Endianness::BigEndian),
            _ => Err(())
        }
    }

    pub fn pointer_size(&self) -> Result<usize,()>{
        match self.pointer_size as char{
            '_' => Ok(32),
            '-' => Ok(64),
            _ => Err(())
        }
    }

    pub fn pointer_bytes(&self) -> Result<usize,()>{
        match self.pointer_size as char{
            '_' => Ok(4),
            '-' => Ok(8),
            _ => Err(())
        }
    }

    pub fn version_number(&self) -> &str{
        str::from_utf8(&self.version_number).unwrap()
    }
}

#[derive(Clone)]
pub struct FileDb<'a>{
    pub file_blocks: &'a Vec<FileBlock>,
    pub file_blocks_addr_table: &'a HashMap<u64, usize>,
    pub dna: &'a SDNA
}

#[derive(Clone)]
pub struct File{
    path: Option<PathBuf>,
    header: Header,
    file_blocks: Vec<FileBlock>,
    file_blocks_addr_table: HashMap<u64, usize>,
    dna: SDNA
}

impl File{
    pub fn load<P: AsRef<Path>>(path: P) -> io::Result<File>{
        let blend = fs::File::open(path.as_ref())?;
        let blend = BufReader::new(blend);
        File::read(blend, Some(path))
    }

    pub fn from_bytes(bytes: &[u8]) -> io::Result<File> {
        let path: Option<&str> = None;
        File::read(bytes, path)
    }

    fn read<R: Read, P: AsRef<Path>>(mut blend: R, path: Option<P>) -> io::Result<File> {
        let header = Header::parse(&mut blend)?;
        let mut file_blocks = Vec::new();
        let mut file_blocks_addr_table = HashMap::default();
        let mut dna = None;
        loop{
            let block = FileBlock::parse(&mut blend, header.pointer_size)?;
            match block{
                Some(block) => {
                    if block.code() == "DNA1"{
                        let sdna = SDNA::new(&header, block)
                            .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid SDNA"))?;
                        dna = Some(sdna);
                    }else{
                        file_blocks_addr_table.insert(block.address, file_blocks.len());
                        file_blocks.push(block);
                    }
                }
                _ => match dna{
                        Some(dna) => return Ok(File{
                            path: path.map(|p| p.as_ref().to_owned()),
                            header: header,
                            file_blocks: file_blocks,
                            file_blocks_addr_table: file_blocks_addr_table,
                            //sdna_index_table: sdna_index_table,
                            dna: dna,
                        }),
                        None => return Err(io::Error::new(io::ErrorKind::InvalidData, "No SDNA Found"))
                }
            }
        }
    }

    pub fn file_db(&self) -> FileDb{
        FileDb{
            file_blocks: &self.file_blocks,
            file_blocks_addr_table: &self.file_blocks_addr_table,
            dna: &self.dna,
        }
    }

    pub fn file_blocks(&self) -> &[FileBlock]{
        &self.file_blocks
    }

    pub fn file_path(&self) -> Option<&Path>{
        self.path.as_ref().map(|p| p as &Path)
    }

    pub fn objects(&self, ty: &str) -> Box<dyn Iterator<Item=Object> + '_>{
        if let Some(scene_idx) = self.dna.structure_idx(ty) {
            let file_db = self.file_db();
            let iter = self.file_blocks.iter()
                .filter(move |block| block.sdna_index == scene_idx as i32)
                .map(move |block| block.object(file_db.clone()));
            Box::new(iter)
        }else{
            Box::new(iter::empty())
        }
    }

    pub fn find_object(&self, ty: &str) -> Option<Object>{
        let scene_idx = self.dna.structure_idx(ty)?;
        self.file_blocks.iter()
            .find(|block| block.sdna_index == scene_idx as i32)
            .map(|block| block.object(self.file_db()))
    }

    pub fn object_by_addr(&self, addr: u64) -> Option<Object>{
        match self.file_blocks_addr_table.get(&addr){
            Some(idx) => Some(self.file_blocks[*idx].object(self.file_db())),
            None => None
        }
    }

    pub fn object_by_name(&self, name: &str) -> Option<Object>{
        self.file_blocks.iter()
            .map(|block| block.object(self.file_db()))
            .find(|obj| obj.name().map(|obj_name| obj_name == name).unwrap_or(false))
    }

    pub fn all_objects(&self) -> impl Iterator<Item=Object>{
        let file_db = self.file_db();
        self.file_blocks.iter()
            .map(move |block| block.object(file_db.clone()))
    }

    pub fn dna(&self) -> &SDNA{
        &self.dna
    }

    pub fn header(&self) -> &Header{
        &self.header
    }
}

pub struct Iter<'a>{
    iter: Box<Iterator<Item=Object<'a>> + 'a>
}

impl<'a> Iterator for Iter<'a>{
    type Item = Object<'a>;
    fn next(&mut self) -> Option<Object<'a>>{
        self.iter.next()
    }
}