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
use std::rc::Rc;

use byteorder::{LittleEndian, ReadBytesExt};

mod chunks;
mod metadata;
mod object_reader;
mod pod;
mod property;
mod reader;
mod result;
mod schemas;
mod time_sampling;

pub use chunks::*;
use metadata::MetaData;
pub use object_reader::{ObjectHeader, ObjectReader};
pub use pod::*;
pub use property::*;
pub use reader::{ArchiveReader, FileReader, MemMappedReader};
pub use result::{InternalError, OgawaError, ParsingError, Result, UserError};
pub use schemas::{BasisType, CurvePeriodicity, CurveType, CurvesSchema, Schema, TopologyVariance};
pub use time_sampling::{TimeSampling, TimeSamplingType};

pub struct Archive {
    pub alembic_file_version: u16,
    pub version: u32,
    pub ogawa_file_version: u32,

    pub root_group: GroupChunk,
    pub root_header: ObjectHeader,

    pub time_samplings: Vec<Rc<TimeSampling>>,
    pub max_samples: Vec<i64>,
    pub indexed_meta_data: Vec<MetaData>,
}

impl Archive {
    pub fn new(reader: &mut dyn ArchiveReader) -> Result<Self> {
        let mut magic = vec![0; 5];
        reader.read_exact(&mut magic)?;

        if magic != [0x4f, 0x67, 0x61, 0x77, 0x61] {
            return Err(ParsingError::UnsupportedAlembicFile.into());
        }

        let _frozen = reader.read_u8()? == 0xff;
        let alembic_file_version = reader.read_u16::<LittleEndian>()?;
        if alembic_file_version >= 9999 {
            return Err(ParsingError::UnsupportedAlembicFile.into());
        }
        let group_pos = reader.read_u64::<LittleEndian>()?;

        let root_group = GroupChunk::load(group_pos, false, reader)?;

        if root_group.child_count <= 5
            || !is_data(root_group.children[0] /*  version */)
            || !is_data(root_group.children[1] /*  file version */)
            || !is_group(root_group.children[2] /* root? */)
            || !is_data(root_group.children[3] /*  metadata */)
            || !is_data(root_group.children[4] /*  time sampling */)
            || !is_data(root_group.children[5] /*  indexed metadata */)
        {
            return Err(ParsingError::InvalidAlembicFile.into());
        }

        let version = {
            let data = root_group.load_data(reader, 0)?;
            data.read_u32(0, reader)?
        };

        let ogawa_file_version = {
            let data = root_group.load_data(reader, 1)?;
            data.read_u32(0, reader)?
        };

        let meta_data = {
            let data = root_group.load_data(reader, 3)?;
            let mut buffer = vec![0u8; data.size as usize];
            data.read(0, reader, &mut buffer)?;
            let text = String::from_utf8(buffer).map_err(ParsingError::FromUtf8Error)?;

            MetaData::deserialize(&text)
        };

        let (time_samplings, max_samples) = {
            let data = root_group.load_data(reader, 4)?;
            time_sampling::read_time_samplings_and_max(&data, reader)?
        };

        let indexed_meta_data = {
            let data = root_group.load_data(reader, 5)?;
            metadata::read_indexed_meta_data(&data, reader)?
        };

        let root_header = ObjectHeader {
            name: "ABC".to_owned(),
            full_name: "/".to_owned(),
            meta_data,
        };

        Ok(Archive {
            alembic_file_version,
            version,
            ogawa_file_version,

            root_group,
            root_header,

            time_samplings,
            max_samples,
            indexed_meta_data,
        })
    }

    pub fn load_root_object(&self, reader: &mut dyn ArchiveReader) -> Result<ObjectReader> {
        let group = self.root_group.load_group(reader, 2, false)?;
        ObjectReader::new(
            group,
            "",
            reader,
            &self.indexed_meta_data,
            &self.time_samplings,
            self.root_header.clone(),
        )
    }
}