fbx_direct 0.6.4

Low-level FBX library
Documentation
//! Contains implementation of Binary FBX parser.

use flate2;

use super::CommonState;
use crate::common::OwnedProperty;
use crate::reader::error::{Error, ErrorKind, Result};
use crate::reader::FbxEvent;
use log::warn;
use std::io::Read;

/// A parser for Binary FBX.
#[derive(Debug, Clone)]
pub struct BinaryParser {
    version: u32,
    end_offset_stack: Vec<u64>,
}

impl BinaryParser {
    /// Constructs Binary FBX parser with FBX version (which is placed after magic binary).
    pub(crate) fn new(version: u32) -> Self {
        BinaryParser {
            version,
            end_offset_stack: vec![],
        }
    }

    pub(crate) fn next<R: Read>(
        &mut self,
        reader: &mut R,
        common: &mut CommonState,
    ) -> Result<FbxEvent> {
        // Check if the previously read node ends here.
        if let Some(&end_pos_top) = self.end_offset_stack.last() {
            if end_pos_top as u64 == common.pos {
                // Reached the end of previously read node.
                self.end_offset_stack.pop();
                return Ok(FbxEvent::EndNode);
            }
        }

        // Read a node record header.
        let node_record_header = NodeRecordHeader::read(reader, &mut common.pos, self)?;
        if node_record_header.is_null_record() {
            // End of a node.
            return if let Some(expected_pos) = self.end_offset_stack.pop() {
                if common.pos == expected_pos as u64 {
                    Ok(FbxEvent::EndNode)
                } else {
                    // Data is collapsed (the node doesn't end at expected position).
                    Err(Error::new(
                        common.pos,
                        ErrorKind::DataError(format!(
                            "Node does not end at expected position (expected {}, now at {})",
                            expected_pos, common.pos
                        )),
                    ))
                }
            } else {
                // Reached end of all nodes.
                // (Extra NULL-record header is end marker of implicit root node.)
                // Footer with unknown contents follows.
                // TODO: Read footer.
                //       Files exported by official products or SDK have padding and their file
                //       sizes are multiple of 16, but some files exported by third-party apps
                //       (such as blender) does not.
                //       So it may be difficult to check if the footer is correct or wrong.
                // NOTE: There is the only thing known, the last 16 bytes of the data always seem
                //       to be `[0xf8, 0x5a, 0x8c, 0x6a, 0xde, 0xf5, 0xd9, 0x7e, 0xec, 0xe9, 0x0c,
                //       0xe3, 0x75, 0x8f, 0x29, 0x0b]`.
                Ok(FbxEvent::EndFbx)
            };
        } else {
            // Start of a node.
            self.end_offset_stack.push(node_record_header.end_offset);
        }

        // Read a node name.
        let name = try_read_fixstr!(common.pos, reader, node_record_header.name_len);

        // Read properties.
        let mut properties =
            Vec::<OwnedProperty>::with_capacity(node_record_header.num_properties as usize);
        for _ in 0..node_record_header.num_properties {
            let prop = self.read_property(reader, common)?;
            properties.push(prop);
        }

        Ok(FbxEvent::StartNode { name, properties })
    }

    /// Read a node property value.
    fn read_property<R: Read>(
        &mut self,
        reader: &mut R,
        common: &mut CommonState,
    ) -> Result<OwnedProperty> {
        let type_code = try_read_le_u8!(common.pos, reader);
        // type code must be ASCII.
        let type_code = if type_code > 0x80 {
            return Err(Error::new(
                common.pos - 1,
                ErrorKind::DataError(format!(
                    "Expected property type code (ASCII) but got {:#x}",
                    type_code
                )),
            ));
        } else {
            type_code as char
        };
        let value = match type_code {
            // 1 bit boolean (1: true, 0: false) encoded as the LSB of a 1 byte value.
            'C' => {
                let val = try_read_le_u8!(common.pos, reader);
                // It seems 'T' (0x54) is used as `false`, 'T' (0x59) is used as `true`.
                if (val != b'T') && (val != b'Y') {
                    // Should this treated as error?
                    // (I don't know whether other characters than 'T' and 'Y' are allowed...)
                    warn!("Expected 'T' or 'Y' for representaton of boolean property value, but got {:#x}", val);
                }
                // Check LSB.
                OwnedProperty::Bool(val & 1 == 1)
            }
            // 2 byte signed integer.
            'Y' => OwnedProperty::I16(try_read_le_i16!(common.pos, reader)),
            // 4 byte signed integer.
            'I' => OwnedProperty::I32(try_read_le_i32!(common.pos, reader)),
            // 4 byte single-precision IEEE 754 floating-point number.
            'F' => OwnedProperty::F32(try_read_le_f32!(common.pos, reader)),
            // 8 byte double-precision IEEE 754 floating-point number.
            'D' => OwnedProperty::F64(try_read_le_f64!(common.pos, reader)),
            // 8 byte signed integer.
            'L' => OwnedProperty::I64(try_read_le_i64!(common.pos, reader)),
            // Array types
            'f' | 'd' | 'l' | 'i' | 'b' => {
                let array_header = PropertyArrayHeader::read(reader, &mut common.pos)?;
                self.read_property_value_array(reader, common, type_code, &array_header)?
            }
            // String
            'S' => {
                let length = try_read_le_u32!(common.pos, reader);
                OwnedProperty::String(try_read_fixstr!(common.pos, reader, length))
            }
            // Raw binary data
            'R' => {
                let length = try_read_le_u32!(common.pos, reader);
                OwnedProperty::Binary(try_read_exact!(common.pos, reader, length))
            }
            _ => {
                return Err(Error::new(
                    common.pos,
                    ErrorKind::UnexpectedValue(format!(
                        "Unsupported type code appears in node property: type_code={}({:#x})",
                        type_code, type_code as u8
                    )),
                ));
            }
        };
        Ok(value)
    }

    /// Read a property value of array type from given stream which maybe compressed.
    fn read_property_value_array<R: Read>(
        &mut self,
        reader: &mut R,
        common: &mut CommonState,
        type_code: char,
        array_header: &PropertyArrayHeader,
    ) -> Result<OwnedProperty> {
        match array_header.encoding {
            // 0; raw
            0 => {
                let (val, byte_size) = self.read_property_value_array_from_plain_stream(
                    reader,
                    common.pos,
                    type_code,
                    array_header.array_length,
                )?;
                common.pos += byte_size;
                Ok(val)
            }
            // 1: zlib compressed data
            1 => {
                let mut decoded_stream = flate2::read::ZlibDecoder::new(
                    reader
                        .by_ref()
                        .take(u64::from(array_header.compressed_length)),
                );
                let (val, _) = self.read_property_value_array_from_plain_stream(
                    &mut decoded_stream,
                    common.pos,
                    type_code,
                    array_header.array_length,
                )?;
                common.pos += u64::from(array_header.compressed_length);
                Ok(val)
            }
            // Unknown.
            e => Err(Error::new(
                common.pos,
                ErrorKind::UnexpectedValue(format!(
                    "Unsupported property array encoding, got {:#x}",
                    e
                )),
            )),
        }
    }

    /// Read a property value of array type from plain (uncompressed) stream.
    fn read_property_value_array_from_plain_stream<R: Read>(
        &mut self,
        reader: &mut R,
        abs_pos: u64,
        type_code: char,
        num_elements: u32,
    ) -> Result<(OwnedProperty, u64)> {
        use byteorder::{LittleEndian, ReadBytesExt};
        Ok(match type_code {
            // Array of 4 byte single-precision IEEE 754 floating-point number.
            'f' => {
                let mut data = Vec::<f32>::with_capacity(num_elements as usize);
                for _ in 0..num_elements {
                    data.push(try_with_pos!(abs_pos, reader.read_f32::<LittleEndian>()));
                }
                (OwnedProperty::VecF32(data), u64::from(num_elements) * 4)
            }
            // Array of 8 byte double-precision IEEE 754 floating-point number.
            'd' => {
                let mut data = Vec::<f64>::with_capacity(num_elements as usize);
                for _ in 0..num_elements {
                    data.push(try_with_pos!(abs_pos, reader.read_f64::<LittleEndian>()));
                }
                (OwnedProperty::VecF64(data), u64::from(num_elements) * 8)
            }
            // Array of 8 byte signed integer.
            'l' => {
                let mut data = Vec::<i64>::with_capacity(num_elements as usize);
                for _ in 0..num_elements {
                    data.push(try_with_pos!(abs_pos, reader.read_i64::<LittleEndian>()));
                }
                (OwnedProperty::VecI64(data), u64::from(num_elements) * 8)
            }
            // Array of 4 byte signed integer.
            'i' => {
                let mut data = Vec::<i32>::with_capacity(num_elements as usize);
                for _ in 0..num_elements {
                    data.push(try_with_pos!(abs_pos, reader.read_i32::<LittleEndian>()));
                }
                (OwnedProperty::VecI32(data), u64::from(num_elements) * 4)
            }
            // Array of 1 byte booleans (always 0 or 1?).
            'b' => {
                let mut data = Vec::<bool>::with_capacity(num_elements as usize);
                for _ in 0..num_elements {
                    // Check LSB.
                    data.push(try_with_pos!(abs_pos, reader.read_u8()) & 1 == 1);
                }
                (OwnedProperty::VecBool(data), u64::from(num_elements))
            }
            _ => {
                // Unreachable because `read_property()` gives only 'f' , 'd', 'l', 'i', or 'b' to
                // `read_property_value_array()`.
                unreachable!();
            }
        })
    }
}

/// A header of a node.
#[derive(Debug, Copy, Clone)]
struct NodeRecordHeader {
    /// Position of the end of the node.
    end_offset: u64,
    /// Number of the properties the node has.
    num_properties: u64,
    /// Byte size of properties of the node in the FBX stream.
    property_list_len: u64,
    /// Byte size of the node name.
    name_len: u8,
}

impl NodeRecordHeader {
    /// Constructs `NodeRecordHeader` from the given stream.
    pub fn read<R: Read>(reader: &mut R, pos: &mut u64, context: &BinaryParser) -> Result<Self> {
        let end_offset = if context.version < 7500 {
            u64::from(try_read_le_u32!(*pos, reader))
        } else {
            try_read_le_u64!(*pos, reader)
        };
        let num_properties = if context.version < 7500 {
            u64::from(try_read_le_u32!(*pos, reader))
        } else {
            try_read_le_u64!(*pos, reader)
        };
        let property_list_len = if context.version < 7500 {
            u64::from(try_read_le_u32!(*pos, reader))
        } else {
            try_read_le_u64!(*pos, reader)
        };
        let name_len = try_read_le_u8!(*pos, reader);
        Ok(NodeRecordHeader {
            end_offset,
            num_properties,
            property_list_len,
            name_len,
        })
    }

    /// Check whether the header indicates there are no more children.
    pub fn is_null_record(&self) -> bool {
        self.end_offset == 0
            && self.num_properties == 0
            && self.property_list_len == 0
            && self.name_len == 0
    }
}

/// A header of a property of array type.
#[derive(Debug, Copy, Clone)]
struct PropertyArrayHeader {
    /// Number of values in the array, *NOT byte size*.
    array_length: u32,
    /// Denotes whether data in stream is plain, or what algorithm it is compressed by.
    encoding: u32,
    /// Byte size of the compressed array value in the stream.
    compressed_length: u32,
}

impl PropertyArrayHeader {
    /// Constructs `PropertyArrayHeader` from the given stream.
    pub fn read<R: Read>(reader: &mut R, pos: &mut u64) -> Result<Self> {
        let array_length = try_read_le_u32!(*pos, reader);
        let encoding = try_read_le_u32!(*pos, reader);
        let compressed_length = try_read_le_u32!(*pos, reader);
        Ok(PropertyArrayHeader {
            array_length,
            encoding,
            compressed_length,
        })
    }
}