use crate::*;
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct Spatial {
pub st3d: Option<St3d>,
pub sv3d: Option<Sv3d>,
pub vexu: Option<Vexu>,
pub hfov: Option<Hfov>,
pub unknown: Vec<Any>,
}
impl Eq for Spatial {}
impl Spatial {
pub(crate) fn decode_child<T: Atom>(
spatial: &mut Option<Box<Spatial>>,
atom: Any,
) -> Result<()> {
match atom {
Any::Unknown(kind @ (St3d::KIND | Sv3d::KIND | Vexu::KIND | Hfov::KIND), body) => {
let spatial: &mut Spatial = spatial.get_or_insert_with(Default::default);
let unknown = &mut spatial.unknown;
match kind {
St3d::KIND => decode_slot(T::KIND, &mut spatial.st3d, &body, unknown),
Sv3d::KIND => decode_slot(T::KIND, &mut spatial.sv3d, &body, unknown),
Vexu::KIND => decode_slot(T::KIND, &mut spatial.vexu, &body, unknown),
_ => decode_slot(T::KIND, &mut spatial.hfov, &body, unknown),
}
Ok(())
}
atom => T::decode_unknown(&atom),
}
}
}
impl Encode for Spatial {
fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
self.st3d.encode(buf)?;
encode_kept(&self.unknown, |kind| kind == St3d::KIND, buf)?;
self.sv3d.encode(buf)?;
encode_kept(&self.unknown, |kind| kind == Sv3d::KIND, buf)?;
self.vexu.encode(buf)?;
encode_kept(&self.unknown, |kind| kind == Vexu::KIND, buf)?;
self.hfov.encode(buf)?;
encode_kept(&self.unknown, |kind| kind == Hfov::KIND, buf)?;
encode_kept(
&self.unknown,
|kind| !matches!(kind, St3d::KIND | Sv3d::KIND | Vexu::KIND | Hfov::KIND),
buf,
)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct FullBoxV0;
impl Ext for FullBoxV0 {
fn encode(&self) -> Result<u32> {
Ok(0)
}
fn decode(v: u32) -> Result<Self> {
match v {
0 => Ok(Self),
v if v >> 24 != 0 => Err(Error::UnknownVersion((v >> 24) as u8)),
_ => Err(Error::Unsupported("non-zero FullBox flags")),
}
}
}
pub(super) fn ignore_trailing<B: Buf>(kind: FourCC, buf: &mut B) {
if buf.has_remaining() {
tracing::warn!(%kind, bytes = buf.remaining(), "ignoring trailing bytes");
buf.advance(buf.remaining());
}
}
pub(super) fn decode_children<B: Buf>(
buf: &mut B,
mut child: impl FnMut(FourCC, &[u8]),
) -> Result<()> {
while let Some(header) = Header::decode_maybe(buf)? {
let size = header.size.unwrap_or(buf.remaining());
if size > buf.remaining() {
return Err(Error::OutOfBounds);
}
child(header.kind, buf.slice(size));
buf.advance(size);
}
skip_trailing_padding(buf);
Ok(())
}
pub(super) fn decode_slot<T: Atom>(
parent: FourCC,
slot: &mut Option<T>,
body: &[u8],
unknown: &mut Vec<Any>,
) {
decode_variant(parent, slot, body, unknown, |atom: T| atom)
}
pub(super) fn decode_variant<T: Atom, S>(
parent: FourCC,
slot: &mut Option<S>,
body: &[u8],
unknown: &mut Vec<Any>,
wrap: impl FnOnce(T) -> S,
) {
let decoded = match slot {
Some(_) => Err(Error::DuplicateBox(T::KIND)),
None => decode_exact::<T>(body),
};
match decoded {
Ok(atom) => *slot = Some(wrap(atom)),
Err(err) => keep_invalid(parent, T::KIND, body, err, unknown),
}
}
fn decode_exact<T: Atom>(mut body: &[u8]) -> Result<T> {
let atom = T::decode_body(&mut body)?;
match body.is_empty() {
true => Ok(atom),
false => Err(Error::UnderDecode(T::KIND)),
}
}
pub(super) fn keep_invalid(
parent: FourCC,
kind: FourCC,
body: &[u8],
err: Error,
unknown: &mut Vec<Any>,
) {
tracing::warn!(%kind, %parent, %err, "keeping spatial box verbatim");
unknown.push(Any::Unknown(kind, body.to_vec()));
}
pub(super) fn encode_kept<B: BufMut>(
unknown: &[Any],
kind: impl Fn(FourCC) -> bool,
buf: &mut B,
) -> Result<()> {
unknown
.iter()
.filter(|atom| kind(atom.kind()))
.try_for_each(|atom| atom.encode(buf))
}
macro_rules! spatial_children {
($($field:ident: $atom:ident),* $(; last $last:ident: $last_atom:ident)? $(,)?) => {
fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
let mut atom = Self::default();
decode_children(buf, |kind, body| match kind {
$($atom::KIND => decode_slot(Self::KIND, &mut atom.$field, body, &mut atom.unknown),)*
$($last_atom::KIND => decode_slot(Self::KIND, &mut atom.$last, body, &mut atom.unknown),)?
kind => atom.unknown.push(Any::Unknown(kind, body.to_vec())),
})?;
Ok(atom)
}
fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
$(
self.$field.encode(buf)?;
encode_kept(&self.unknown, |kind| kind == $atom::KIND, buf)?;
)*
encode_kept(
&self.unknown,
|kind| !matches!(kind, $($atom::KIND)|* $(| $last_atom::KIND)?),
buf,
)?;
$(
self.$last.encode(buf)?;
encode_kept(&self.unknown, |kind| kind == $last_atom::KIND, buf)?;
)?
Ok(())
}
};
}
pub(super) use spatial_children;