2021-11-21 20:18:19 +00:00
|
|
|
use crate::error::{LoftyError, Result};
|
|
|
|
|
|
|
|
use std::io::{Read, Seek, SeekFrom};
|
|
|
|
|
|
|
|
use crate::mp4::AtomIdent;
|
|
|
|
use byteorder::{BigEndian, ReadBytesExt};
|
|
|
|
|
|
|
|
pub(crate) struct AtomInfo {
|
|
|
|
pub(crate) start: u64,
|
|
|
|
pub(crate) len: u64,
|
|
|
|
pub(crate) extended: bool,
|
|
|
|
pub(crate) ident: AtomIdent,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AtomInfo {
|
|
|
|
pub(crate) fn read<R>(data: &mut R) -> Result<Self>
|
|
|
|
where
|
|
|
|
R: Read + Seek,
|
|
|
|
{
|
|
|
|
let start = data.seek(SeekFrom::Current(0))?;
|
|
|
|
|
|
|
|
let len = data.read_u32::<BigEndian>()?;
|
|
|
|
|
|
|
|
let mut ident = [0; 4];
|
|
|
|
data.read_exact(&mut ident)?;
|
|
|
|
|
|
|
|
let mut atom_ident = AtomIdent::Fourcc(ident);
|
|
|
|
|
|
|
|
// Encountered a freeform identifier
|
|
|
|
if &ident == b"----" {
|
|
|
|
atom_ident = parse_freeform(data)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let (len, extended) = match len {
|
|
|
|
// The atom extends to the end of the file
|
|
|
|
0 => {
|
|
|
|
let pos = data.seek(SeekFrom::Current(0))?;
|
|
|
|
let end = data.seek(SeekFrom::End(0))?;
|
|
|
|
|
|
|
|
data.seek(SeekFrom::Start(pos))?;
|
|
|
|
|
|
|
|
(end - pos, false)
|
2021-11-22 10:14:29 +00:00
|
|
|
},
|
2021-11-21 20:18:19 +00:00
|
|
|
// There's an extended length
|
|
|
|
1 => (data.read_u64::<BigEndian>()?, true),
|
|
|
|
_ if len < 8 => return Err(LoftyError::BadAtom("Found an invalid length (< 8)")),
|
|
|
|
_ => (u64::from(len), false),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
start,
|
|
|
|
len,
|
|
|
|
extended,
|
|
|
|
ident: atom_ident,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_freeform<R>(data: &mut R) -> Result<AtomIdent>
|
|
|
|
where
|
|
|
|
R: Read + Seek,
|
|
|
|
{
|
|
|
|
let mean = freeform_chunk(data, b"mean")?;
|
|
|
|
let name = freeform_chunk(data, b"name")?;
|
|
|
|
|
|
|
|
Ok(AtomIdent::Freeform { mean, name })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn freeform_chunk<R>(data: &mut R, name: &[u8]) -> Result<String>
|
|
|
|
where
|
|
|
|
R: Read + Seek,
|
|
|
|
{
|
|
|
|
let atom = AtomInfo::read(data)?;
|
|
|
|
|
|
|
|
match atom.ident {
|
|
|
|
AtomIdent::Fourcc(ref fourcc) if fourcc == name => {
|
|
|
|
// Version (1)
|
|
|
|
// Flags (3)
|
|
|
|
data.seek(SeekFrom::Current(4))?;
|
|
|
|
|
|
|
|
// Already read the size, identifier, and version/flags (12 bytes)
|
|
|
|
let mut content = vec![0; (atom.len - 12) as usize];
|
|
|
|
data.read_exact(&mut content)?;
|
|
|
|
|
|
|
|
String::from_utf8(content).map_err(|_| {
|
|
|
|
LoftyError::BadAtom("Found a non UTF-8 string while reading freeform identifier")
|
|
|
|
})
|
2021-11-22 10:14:29 +00:00
|
|
|
},
|
2021-11-21 20:18:19 +00:00
|
|
|
_ => Err(LoftyError::BadAtom(
|
|
|
|
"Found freeform identifier \"----\" with no trailing \"mean\" or \"name\" atoms",
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|