use std::{error, fmt, io};
use log::info;
use crate::{low::FbxVersion, pull_parser::ParserVersion};
const MAGIC_LEN: usize = 23;
pub(crate) const MAGIC: &[u8; MAGIC_LEN] = b"Kaydara FBX Binary \x00\x1a\x00";
#[derive(Debug)]
pub enum HeaderError {
Io(io::Error),
MagicNotDetected,
}
impl error::Error for HeaderError {}
impl fmt::Display for HeaderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HeaderError::Io(e) => e.fmt(f),
HeaderError::MagicNotDetected => f.write_str("FBX magic binary is not detected"),
}
}
}
impl From<io::Error> for HeaderError {
#[inline]
fn from(e: io::Error) -> Self {
HeaderError::Io(e)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FbxHeader {
version: FbxVersion,
}
impl FbxHeader {
pub fn load(mut reader: impl io::Read) -> Result<Self, HeaderError> {
let mut magic_buf = [0u8; MAGIC_LEN];
reader.read_exact(&mut magic_buf)?;
if magic_buf != *MAGIC {
return Err(HeaderError::MagicNotDetected);
}
let version = {
let mut buf = [0_u8; 4];
reader.read_exact(&mut buf)?;
u32::from_le_bytes(buf)
};
info!("FBX header is detected, version={}", version);
Ok(FbxHeader {
version: FbxVersion::new(version),
})
}
#[inline]
#[must_use]
pub fn version(self) -> FbxVersion {
self.version
}
#[inline]
#[must_use]
pub fn parser_version(self) -> Option<ParserVersion> {
ParserVersion::from_fbx_version(self.version())
}
#[inline]
#[must_use]
pub(crate) const fn len(self) -> usize {
const VERSION_LEN: usize = 4;
MAGIC_LEN + VERSION_LEN
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{Cursor, Read};
#[test]
fn header_ok() {
let raw_header = b"Kaydara FBX Binary \x00\x1a\x00\xe8\x1c\x00\x00";
let mut cursor = Cursor::new(raw_header);
let header = FbxHeader::load(cursor.by_ref()).expect("Should never fail");
assert_eq!(
header.version(),
FbxVersion::new(7400),
"Header and version should be detected correctly"
);
assert_eq!(
cursor.position() as usize,
raw_header.len(),
"Header should be read completely"
);
}
#[test]
fn magic_ng() {
let wrong_header = b"Kaydara FBX Binary \x00\xff\x00\xe8\x1c\x00\x00";
let mut cursor = Cursor::new(wrong_header);
assert!(
matches!(
FbxHeader::load(cursor.by_ref()),
Err(HeaderError::MagicNotDetected)
),
"Invalid magic should be reported by `MagicNotDetected`"
);
assert!(
(cursor.position() as usize) < wrong_header.len(),
"Header should not be read too much if the magic is not detected"
);
}
}