use std::{
any::type_name,
collections::HashMap,
io::{self, Cursor},
mem, str, vec,
};
use crate::gf::f16;
use bytemuck::{AnyBitPattern, NoUninit, Pod, bytes_of, bytes_of_mut, cast_slice_mut};
use num_traits::{AsPrimitive, Float, PrimInt};
use crate::{
gf,
sdf::{self, Value},
tf,
usdc::coding,
};
use super::layout::*;
use super::{ReadError, ReadResultExt};
macro_rules! corrupt {
($cond:expr) => {
corrupt!($cond, "{}", stringify!($cond))
};
($cond:expr, $($arg:tt)*) => {
if !$cond {
return Err(ReadError::corrupt(format!($($arg)*)));
}
};
}
const SW_VERSION: Version = version(0, 12, 0);
#[derive(Debug)]
pub struct CrateFile<R> {
reader: R,
pub bootstrap: Bootstrap,
pub sections: Vec<Section>,
pub tokens: Vec<String>,
pub strings: Vec<usize>,
pub fields: Vec<Field>,
pub fieldsets: Vec<Option<usize>>,
pub paths: Vec<sdf::Path>,
pub specs: Vec<Spec>,
}
impl<R> CrateFile<R> {
#[inline]
pub fn version(&self) -> Version {
Version::from(self.bootstrap)
}
}
impl<R: io::Read + io::Seek> CrateFile<R> {
pub fn open(mut reader: R) -> Result<Self, ReadError> {
let bootstrap = Self::read_header(&mut reader)?;
let mut file = CrateFile {
reader,
bootstrap,
sections: Vec::new(),
tokens: Vec::new(),
strings: Vec::new(),
fields: Vec::new(),
fieldsets: Vec::new(),
paths: Vec::new(),
specs: Vec::new(),
};
file.read_sections().ctx("sections")?;
file.read_tokens().ctx("TOKENS section")?;
file.read_strings().ctx("STRINGS section")?;
file.read_fields().ctx("FIELDS section")?;
file.read_fieldsets().ctx("FIELDSETS section")?;
file.read_paths().ctx("PATHS section")?;
file.read_specs().ctx("SPECS section")?;
Ok(file)
}
pub fn validate(&self) -> Result<(), ReadError> {
for (index, field) in self.fields.iter().enumerate() {
corrupt!(
self.tokens.get(field.token_index).is_some(),
"Invalid field token index {}: {}",
index,
field.token_index
);
}
for (index, fieldset) in self
.fieldsets
.iter()
.enumerate()
.filter_map(|(i, index)| index.map(|index| (i, index)))
{
corrupt!(
self.fields.get(fieldset).is_some(),
"Invalid fieldset index {index}: {fieldset}"
);
}
for (index, spec) in self.specs.iter().enumerate() {
corrupt!(
self.paths.get(spec.path_index).is_some(),
"Invalid spec {} path index: {}",
index,
spec.path_index
);
corrupt!(
self.fieldsets.get(spec.fieldset_index).is_some(),
"Invalid spec {} fieldset index: {}",
index,
spec.fieldset_index
);
if spec.fieldset_index > 0 {
corrupt!(
self.fieldsets[spec.fieldset_index - 1].is_none(),
"Invalid spec {}, the element at the prior index {} must be a default-constructed field index",
index,
spec.fieldset_index
);
}
corrupt!(spec.spec_type != sdf::SpecType::Unknown, "Invalid spec {index} type");
}
Ok(())
}
fn read_header(mut reader: impl io::Read + io::Seek) -> Result<Bootstrap, ReadError> {
let header = reader.read_pod::<Bootstrap>()?;
corrupt!(header.ident.eq(super::MAGIC), "Usd crate bootstrap section corrupt");
corrupt!(header.toc_offset > 0, "Invalid TOC offset");
let file_ver = version(header.version[0], header.version[1], header.version[2]);
if !SW_VERSION.can_read(file_ver) {
return Err(ReadError::unsupported(format!(
"Usd crate version mismatch, file is {file_ver}, library supports {SW_VERSION}"
)));
}
Ok(header)
}
fn read_sections(&mut self) -> Result<(), ReadError> {
self.set_position(self.bootstrap.toc_offset)?;
let count = self.reader.read_count()?;
corrupt!(count > 0, "Crate file has no sections");
corrupt!(count < 64, "Suspiciously large number of sections: {count}");
self.sections = self.reader.read_vec::<Section>(count)?;
Ok(())
}
fn read_tokens(&mut self) -> Result<(), ReadError> {
let Some(section) = self.find_section(Section::TOKENS) else {
return Ok(());
};
self.set_position(section.start)?;
let file_ver = self.version();
let count = self.reader.read_count()?;
self.tokens = if file_ver < version(0, 4, 0) {
todo!("Support TOKENS reader for < 0.4.0 files");
} else {
let uncompressed_size = self.reader.read_count()?;
let mut buffer = self.read_compressed(uncompressed_size)?;
corrupt!(
buffer.len() == uncompressed_size,
"Decompressed size mismatch (expected {}, got {})",
uncompressed_size,
buffer.len(),
);
if buffer.is_empty() {
corrupt!(
count == 0,
"Tokens section claims {count} tokens but the buffer is empty"
);
Vec::new()
} else {
corrupt!(
buffer.last() == Some(&b'\0'),
"Tokens section not null-terminated in crate file"
);
buffer.pop();
let strings = buffer
.split(|c| *c == b'\0')
.map(|buf| str::from_utf8(buf).map(|str| str.to_string()))
.collect::<Result<Vec<_>, str::Utf8Error>>()?;
corrupt!(
strings.len() == count,
"Crate file claims {} tokens, but found {}",
count,
strings.len(),
);
strings
}
};
Ok(())
}
fn read_strings(&mut self) -> Result<(), ReadError> {
let Some(section) = self.find_section(Section::STRINGS) else {
return Ok(());
};
self.set_position(section.start)?;
let count = self.reader.read_count()?;
corrupt!(
count < 128 * 1024 * 1024,
"Suspiciously large number of strings: {count}"
);
let strings = self.reader.read_vec::<u32>(count)?;
self.strings = strings.into_iter().map(|offset| offset as usize).collect::<Vec<_>>();
Ok(())
}
fn read_fields(&mut self) -> Result<(), ReadError> {
let Some(section) = self.find_section(Section::FIELDS) else {
return Ok(());
};
self.set_position(section.start)?;
let file_ver = self.version();
self.fields = if file_ver < version(0, 4, 0) {
todo!("Support FIELDS reader before < 0.4.0")
} else {
let field_count = self.reader.read_count()?;
let indices = self.read_encoded_ints(field_count)?;
let reps = self.read_compressed(field_count)?;
let fields: Vec<_> = indices
.iter()
.zip(reps.iter())
.map(|(index, value)| Field::new(*index, *value))
.collect();
debug_assert_eq!(fields.len(), field_count);
fields
};
Ok(())
}
fn read_fieldsets(&mut self) -> Result<(), ReadError> {
let Some(section) = self.find_section(Section::FIELDSETS) else {
return Ok(());
};
self.set_position(section.start)?;
let file_ver = self.version();
self.fieldsets = if file_ver < version(0, 4, 0) {
todo!("Support FIELDSETS reader for < 0.4.0 files");
} else {
let count = self.reader.read_count()?;
let decoded = self.read_encoded_ints::<u32>(count)?;
const INVALID_INDEX: u32 = u32::MAX;
let sets = decoded
.into_iter()
.map(|i| if i == INVALID_INDEX { None } else { Some(i as usize) })
.collect::<Vec<_>>();
debug_assert_eq!(sets.len(), count);
sets
};
Ok(())
}
fn read_paths(&mut self) -> Result<(), ReadError> {
let Some(section) = self.find_section(Section::PATHS) else {
return Ok(());
};
self.set_position(section.start)?;
let file_ver = self.version();
if file_ver == version(0, 0, 1) {
todo!("Support PATHS reader for == 0.0.1 files");
} else if file_ver < version(0, 4, 0) {
todo!("Support PATHS reader for < 0.4.0 files");
} else {
let path_count = self.reader.read_count()?;
self.paths = vec![sdf::Path::default(); path_count];
self.read_compressed_paths()?;
};
Ok(())
}
fn read_compressed_paths(&mut self) -> Result<(), ReadError> {
let count: usize = self.reader.read_count()?;
let path_indexes = self.read_encoded_ints::<u32>(count)?;
debug_assert_eq!(path_indexes.len(), count);
let element_token_indexes = self.read_encoded_ints::<i32>(count)?;
debug_assert_eq!(element_token_indexes.len(), count);
let jumps = self.read_encoded_ints::<i32>(count)?;
debug_assert_eq!(jumps.len(), count);
self.build_compressed_paths(&path_indexes, &element_token_indexes, &jumps)?;
Ok(())
}
fn build_compressed_paths(
&mut self,
path_indexes: &[u32],
element_token_indexes: &[i32],
jumps: &[i32],
) -> Result<(), ReadError> {
if path_indexes.is_empty() {
return Ok(());
}
let mut pending = vec![(0usize, sdf::Path::default())];
while let Some((mut current_index, mut parent_path)) = pending.pop() {
loop {
let index = current_index;
current_index += 1;
if parent_path.is_empty() {
parent_path = sdf::Path::new("/")?;
checked_path_slot(&self.paths, index)?;
self.paths[index] = parent_path.clone();
} else {
let token_index = *indexed(element_token_indexes, index, "path element")?;
let is_prim_property_path = token_index < 0;
let token_index = token_index.unsigned_abs() as usize;
let element_token = indexed(&self.tokens, token_index, "token")?.as_str();
let slot = *indexed(path_indexes, index, "path")? as usize;
checked_path_slot(&self.paths, slot)?;
self.paths[slot] = if is_prim_property_path {
parent_path.append_property(element_token)?
} else if element_token.starts_with('{') {
parent_path.append_variant_segment(element_token)?
} else {
parent_path.append_path(element_token)?
};
}
let jump = *indexed(jumps, index, "path jump")?;
let has_child = jump > 0 || jump == -1;
let has_sibling = jump >= 0;
if has_child {
if has_sibling {
let sibling_index = index + jump as usize;
pending.push((sibling_index, parent_path.clone()));
}
let slot = *indexed(path_indexes, index, "path")? as usize;
parent_path = indexed(&self.paths, slot, "path")?.clone();
}
if !has_child && !has_sibling {
break;
}
}
}
Ok(())
}
fn read_specs(&mut self) -> Result<(), ReadError> {
let Some(section) = self.find_section(Section::SPECS) else {
return Ok(());
};
self.set_position(section.start)?;
let file_ver = self.version();
self.specs = if file_ver == version(0, 0, 1) {
todo!("Support SPECS reader for == 0.0.1 files");
} else if file_ver < version(0, 4, 0) {
todo!("Support SPECS reader for < 0.4.0 files");
} else {
let spec_count = self.reader.read_count()?;
let path_indexes = self.read_encoded_ints::<u32>(spec_count)?;
let fieldset_indexes = self.read_encoded_ints::<u32>(spec_count)?;
let spec_types = self.read_encoded_ints::<u32>(spec_count)?;
path_indexes
.into_iter()
.zip(fieldset_indexes)
.zip(spec_types)
.map(|((path, fieldset), spec_type)| {
Ok(Spec {
path_index: path as usize,
fieldset_index: fieldset as usize,
spec_type: sdf::SpecType::from_repr(spec_type)
.ok_or_else(|| ReadError::corrupt(format!("Unable to parse SDF spec type: {spec_type}")))?,
})
})
.collect::<Result<Vec<_>, ReadError>>()?
};
Ok(())
}
pub fn find_section(&self, name: &str) -> Option<&Section> {
self.sections.iter().find(|s| s.name() == name)
}
fn resolve_string(&self, string_index: u32) -> Result<String, ReadError> {
let token = *indexed(&self.strings, string_index as usize, "string")?;
Ok(indexed(&self.tokens, token, "token")?.clone())
}
fn set_position(&mut self, position: u64) -> Result<(), ReadError> {
self.reader.seek(io::SeekFrom::Start(position))?;
Ok(())
}
fn unpack_value<T: Default + Pod>(&mut self, value: ValueRep) -> Result<T, ReadError> {
corrupt!(!value.is_array(), "Can't unpack array {value:?} as inline value");
let ty = value.ty()?;
corrupt!(ty != Type::Invalid, "Invalid value type");
let value = if value.is_inlined() {
let tmp = value.payload() & ((1_u64 << (mem::size_of::<u32>() * 8)) - 1);
let mut cursor = Cursor::new(bytes_of(&tmp));
cursor.read_pod::<T>()?
} else {
self.set_position(value.payload())?;
self.reader.read_pod::<T>()?
};
Ok(value)
}
fn read_token(&mut self, value: ValueRep) -> Result<String, ReadError> {
let index: u64 = self.unpack_value(value)?;
Ok(indexed(&self.tokens, index as usize, "token")?.clone())
}
fn read_asset_path(&mut self, value: ValueRep) -> Result<String, ReadError> {
let index = self.unpack_value::<u32>(value)?;
if value.is_inlined() {
Ok(indexed(&self.tokens, index as usize, "token")?.clone())
} else {
self.resolve_string(index)
}
}
fn read_compressed<T: Default + NoUninit + AnyBitPattern>(
&mut self,
estimated_count: usize,
) -> Result<Vec<T>, ReadError> {
let compressed_size = self.reader.read_count()?;
let mut input = vec![0_u8; compressed_size];
self.reader.read_exact(&mut input)?;
let mut output = vec![T::default(); estimated_count];
let actual_size = decompress_lz4(&input, cast_slice_mut(&mut output))?;
let actual_count = actual_size / mem::size_of::<T>();
if actual_count < output.len() {
output.truncate(actual_count);
}
Ok(output)
}
fn read_encoded_ints<T: PrimInt + 'static>(&mut self, count: usize) -> Result<Vec<T>, ReadError>
where
i64: AsPrimitive<T>,
{
let estimated_size = coding::encoded_buffer_size::<u32>(count);
let buffer = self.read_compressed::<u8>(estimated_size)?;
let ints = coding::decode_ints(buffer.as_slice(), count)?;
debug_assert_eq!(ints.len(), count);
Ok(ints)
}
const MIN_COMPRESSED_ARRAY_SIZE: usize = 4;
fn unpack_array_len(&mut self, value: ValueRep, kind: ArrayKind) -> Result<(usize, bool), ReadError> {
debug_assert!(!value.is_inlined());
if value.payload() == 0 {
return Ok((0, false));
}
self.set_position(value.payload())?;
if self.version() < version(0, 5, 0) {
let _ = self.reader.read_pod::<u32>()?;
}
let mut compressed = true;
match kind {
ArrayKind::Ints => {
if self.version() < version(0, 5, 0) || !value.is_compressed() {
compressed = false;
}
}
ArrayKind::Floats => {
if self.version() < version(0, 6, 0) || !value.is_compressed() {
compressed = false;
}
}
ArrayKind::Other => {
debug_assert!(!value.is_compressed());
compressed = false;
}
}
let count = if self.version() < version(0, 7, 0) {
self.reader.read_pod::<u32>()? as usize
} else {
self.reader.read_pod::<u64>()? as usize
};
if count < Self::MIN_COMPRESSED_ARRAY_SIZE {
compressed = false;
}
Ok((count, compressed))
}
fn read_ints<T: PrimInt + Pod + Default>(&mut self, value: ValueRep) -> Result<Vec<T>, ReadError>
where
i64: AsPrimitive<T>,
{
let (count, compressed) = self.unpack_array_len(value, ArrayKind::Ints)?;
if count == 0 {
return Ok(Vec::default());
}
if compressed {
self.read_encoded_ints(count)
} else {
self.reader.read_vec(count)
}
}
fn read_floats<T: Float + Default + Pod>(&mut self, value: ValueRep) -> Result<Vec<T>, ReadError> {
use num_traits::cast;
corrupt!(!value.is_inlined());
let (count, compressed) = self.unpack_array_len(value, ArrayKind::Floats)?;
let vec = if compressed {
let code = self.reader.read_pod::<u8>()?;
match code {
b'i' => {
let ints: Vec<i32> = self.read_encoded_ints(count)?;
ints.into_iter().map(|i| cast(i).unwrap()).collect()
}
b't' => {
let lut_size = self.reader.read_pod::<u32>()? as usize;
let lut: Vec<T> = self.reader.read_vec(lut_size)?;
let indexes: Vec<u32> = self.read_encoded_ints(count)?;
corrupt!(
indexes.len() == count,
"Read invalid number of indexes to decompress doubles array"
);
let mut output = vec![T::zero(); count];
for (i, index) in indexes.into_iter().enumerate() {
output[i] = lut[index as usize];
}
output
}
_ => {
return Err(ReadError::corrupt(format!(
"Invalid compressed double array code: {code}"
)));
}
}
} else {
self.reader.read_vec(count)?
};
Ok(vec)
}
fn read_list_op<T: Default + Clone + PartialEq>(
&mut self,
value: ValueRep,
mut read: impl FnMut(&mut Self) -> Result<Vec<T>, ReadError>,
) -> Result<sdf::ListOp<T>, ReadError> {
self.set_position(value.payload())?;
let mut out = sdf::ListOp::<T>::default();
let header = self.reader.read_pod::<ListOpHeader>()?;
if header.is_explicit() {
out.explicit = true;
}
if header.has_explicit() {
out.explicit_items = read(self)?;
}
if header.has_added() {
out.added_items = read(self)?;
}
if header.has_prepend() {
out.prepended_items = read(self)?;
}
if header.has_appended() {
out.appended_items = read(self)?;
}
if header.has_deleted() {
out.deleted_items = read(self)?;
}
if header.has_ordered() {
out.ordered_items = read(self)?;
}
Ok(out)
}
fn read_indexed_vec<T>(
&mut self,
lookup: impl Fn(&Self, usize) -> Result<T, ReadError>,
) -> Result<Vec<T>, ReadError> {
let count = self.reader.read_count()?;
let indices = self.reader.read_vec::<u32>(count)?;
indices.into_iter().map(|index| lookup(self, index as usize)).collect()
}
fn read_string_vec(&mut self) -> Result<Vec<String>, ReadError> {
self.read_indexed_vec(|file, index| {
let token = *indexed(&file.strings, index, "string")?;
Ok(indexed(&file.tokens, token, "token")?.clone())
})
}
fn read_token_vec(&mut self) -> Result<Vec<String>, ReadError> {
self.read_indexed_vec(|file, index| Ok(indexed(&file.tokens, index, "token")?.clone()))
}
fn read_path_vec(&mut self) -> Result<Vec<sdf::Path>, ReadError> {
self.read_indexed_vec(|file, index| Ok(indexed(&file.paths, index, "path")?.clone()))
}
fn read_pod_vec<T: Default + NoUninit + AnyBitPattern>(&mut self) -> Result<Vec<T>, ReadError> {
let count = self.reader.read_count()?;
self.reader.read_vec(count)
}
fn read_string(&mut self) -> Result<String, ReadError> {
let index = self.reader.read_pod::<u32>()?;
self.resolve_string(index)
}
fn read_path(&mut self) -> Result<sdf::Path, ReadError> {
let index = self.reader.read_pod::<u32>()?;
Ok(indexed(&self.paths, index as usize, "path")?.clone())
}
fn read_reference(&mut self) -> Result<sdf::Reference, ReadError> {
let asset_path = self.read_string()?;
let prim_path = self.read_path()?;
let layer_offset = self.reader.read_pod::<sdf::LayerOffset>()?;
let custom_data = self.read_custom_data()?;
Ok(sdf::Reference {
asset_path,
prim_path,
layer_offset,
custom_data,
})
}
fn read_payload(&mut self) -> Result<sdf::Payload, ReadError> {
let asset_path = self.read_string()?;
let prim_path = self.read_path()?;
let mut payload = sdf::Payload {
asset_path,
prim_path,
layer_offset: None,
};
if self.version() >= version(0, 8, 0) {
let layer_offset = self.reader.read_pod::<sdf::LayerOffset>()?;
payload.layer_offset = Some(layer_offset);
}
Ok(payload)
}
fn apply_recursive_offset(&mut self) -> Result<(), ReadError> {
let offset = self.reader.read_pod::<i64>()?;
self.reader.seek(io::SeekFrom::Current(offset - 8))?;
Ok(())
}
fn read_custom_data(&mut self) -> Result<HashMap<String, Value>, ReadError> {
let mut count = self.reader.read_count()?;
let mut dict = HashMap::default();
while count > 0 {
let key = self.read_string()?;
let value = {
self.apply_recursive_offset()?;
let value = self.reader.read_pod::<ValueRep>()?;
corrupt!(value.ty()? != Type::Invalid, "Can't parse dictionary value type");
let saved_position = self.reader.stream_position()?;
let value = self.value(value)?;
self.set_position(saved_position)?;
value
};
dict.insert(key, value);
count -= 1;
}
Ok(dict)
}
fn read_gf_array<U: Default + NoUninit + AnyBitPattern>(&mut self, value: ValueRep) -> Result<Vec<U>, ReadError> {
debug_assert!(value.is_array() && !value.is_compressed());
let (count, _) = self.unpack_array_len(value, ArrayKind::Other)?;
if count == 0 {
return Ok(Vec::default());
}
self.reader.read_vec::<U>(count)
}
fn read_vec_array<T: Default + NoUninit + AnyBitPattern, const N: usize>(
&mut self,
value: ValueRep,
) -> Result<Vec<[T; N]>, ReadError> {
debug_assert!(value.is_array());
debug_assert!(!value.is_compressed());
let (count, _) = self.unpack_array_len(value, ArrayKind::Other)?;
if count == 0 {
return Ok(Vec::default());
}
let flat: Vec<T> = self.reader.read_vec(count * N)?;
let result = flat
.chunks_exact(N)
.map(|chunk| chunk.try_into().expect("chunks_exact yields N elements"))
.collect();
Ok(result)
}
pub fn value(&mut self, value: ValueRep) -> Result<sdf::Value, ReadError> {
let ty = value.ty()?;
corrupt!(ty != Type::Invalid, "Invalid value type");
let variant = match ty {
Type::Bool if value.is_array() => {
let vec = self
.read_vec_array::<u8, 1>(value)?
.into_iter()
.map(|[v]| v != 0)
.collect();
sdf::Value::BoolVec(vec)
}
Type::Bool => {
let value: i32 = self.unpack_value(value)?;
sdf::Value::Bool(value != 0)
}
Type::Uchar if value.is_array() => {
let vec = self.read_vec_array::<u8, 1>(value)?.into_iter().map(|[v]| v).collect();
sdf::Value::UcharVec(vec)
}
Type::Uchar => {
let value = self.unpack_value::<u8>(value)?;
sdf::Value::Uchar(value)
}
Type::Int if value.is_array() => sdf::Value::IntVec(self.read_ints(value)?),
Type::Int => sdf::Value::Int(self.unpack_value(value)?),
Type::Uint if value.is_array() => sdf::Value::UintVec(self.read_ints(value)?),
Type::Uint => sdf::Value::Uint(self.unpack_value(value)?),
Type::Int64 if value.is_array() => sdf::Value::Int64Vec(self.read_ints(value)?),
Type::Int64 => sdf::Value::Int64(self.unpack_value(value)?),
Type::Uint64 if value.is_array() => sdf::Value::Uint64Vec(self.read_ints(value)?),
Type::Uint64 => sdf::Value::Uint64(self.unpack_value(value)?),
Type::Half if value.is_array() => sdf::Value::HalfVec(self.read_floats(value)?),
Type::Half => sdf::Value::Half(self.unpack_value(value)?),
Type::Float if value.is_array() => sdf::Value::FloatVec(self.read_floats(value)?),
Type::Float => sdf::Value::Float(self.unpack_value(value)?),
Type::Double if value.is_array() => sdf::Value::DoubleVec(self.read_floats(value)?),
Type::Double if value.is_inlined() => {
let value = self.unpack_value::<f32>(value)?;
sdf::Value::Double(value as f64)
}
Type::Double => sdf::Value::Double(self.unpack_value(value)?),
Type::DoubleVector => sdf::Value::DoubleVec(self.read_floats(value)?),
Type::StringVector => {
corrupt!(!value.is_inlined());
self.set_position(value.payload())?;
sdf::Value::StringVec(self.read_string_vec()?)
}
Type::String if value.is_array() => {
self.set_position(value.payload())?;
sdf::Value::StringVec(self.read_string_vec()?)
}
Type::String => {
corrupt!(!value.is_array());
let string_index = self.unpack_value::<u32>(value)?;
sdf::Value::String(self.resolve_string(string_index)?)
}
Type::AssetPath if value.is_array() => {
self.set_position(value.payload())?;
sdf::Value::AssetPathVec(self.read_string_vec()?.into_iter().map(Into::into).collect())
}
Type::AssetPath => sdf::Value::AssetPath(self.read_asset_path(value)?.into()),
Type::Token if value.is_array() => {
let (count, _) = self.unpack_array_len(value, ArrayKind::Other)?;
let indices = self.reader.read_vec::<u32>(count)?;
let tokens = indices
.into_iter()
.map(|i| Ok(indexed(&self.tokens, i as usize, "token")?.as_str().into()))
.collect::<Result<Vec<_>, ReadError>>()?;
sdf::Value::TokenVec(tokens)
}
Type::Token => sdf::Value::Token(self.read_token(value)?.into()),
Type::Vec2h if value.is_array() => Value::Vec2hVec(self.read_gf_array::<gf::Vec2h>(value)?),
Type::Vec2f if value.is_array() => Value::Vec2fVec(self.read_gf_array::<gf::Vec2f>(value)?),
Type::Vec2d if value.is_array() => Value::Vec2dVec(self.read_gf_array::<gf::Vec2d>(value)?),
Type::Vec2i if value.is_array() => Value::Vec2iVec(self.read_gf_array::<gf::Vec2i>(value)?),
Type::Vec3h if value.is_array() => Value::Vec3hVec(self.read_gf_array::<gf::Vec3h>(value)?),
Type::Vec3f if value.is_array() => Value::Vec3fVec(self.read_gf_array::<gf::Vec3f>(value)?),
Type::Vec3d if value.is_array() => Value::Vec3dVec(self.read_gf_array::<gf::Vec3d>(value)?),
Type::Vec3i if value.is_array() => Value::Vec3iVec(self.read_gf_array::<gf::Vec3i>(value)?),
Type::Vec4h if value.is_array() => Value::Vec4hVec(self.read_gf_array::<gf::Vec4h>(value)?),
Type::Vec4f if value.is_array() => Value::Vec4fVec(self.read_gf_array::<gf::Vec4f>(value)?),
Type::Vec4d if value.is_array() => Value::Vec4dVec(self.read_gf_array::<gf::Vec4d>(value)?),
Type::Vec4i if value.is_array() => Value::Vec4iVec(self.read_gf_array::<gf::Vec4i>(value)?),
Type::Vec2h if value.is_inlined() => sdf::Value::Vec2h(self.unpack_value::<gf::Vec2h>(value)?),
Type::Vec2f if value.is_inlined() => {
let [x, y] = to_vec::<f32, 2>(self.unpack_value(value)?);
sdf::Value::Vec2f(gf::vec2f(x, y))
}
Type::Vec2d if value.is_inlined() => {
let [x, y] = to_vec::<f64, 2>(self.unpack_value(value)?);
sdf::Value::Vec2d(gf::vec2d(x, y))
}
Type::Vec2i if value.is_inlined() => {
let [x, y] = to_vec::<i32, 2>(self.unpack_value(value)?);
sdf::Value::Vec2i(gf::vec2i(x, y))
}
Type::Vec3h if value.is_inlined() => {
let [x, y, z] = to_vec::<f16, 3>(self.unpack_value(value)?);
sdf::Value::Vec3h(gf::vec3h(x, y, z))
}
Type::Vec3f if value.is_inlined() => {
let [x, y, z] = to_vec::<f32, 3>(self.unpack_value(value)?);
sdf::Value::Vec3f(gf::vec3f(x, y, z))
}
Type::Vec3d if value.is_inlined() => {
let [x, y, z] = to_vec::<f64, 3>(self.unpack_value(value)?);
sdf::Value::Vec3d(gf::vec3d(x, y, z))
}
Type::Vec3i if value.is_inlined() => {
let [x, y, z] = to_vec::<i32, 3>(self.unpack_value(value)?);
sdf::Value::Vec3i(gf::vec3i(x, y, z))
}
Type::Vec4h if value.is_inlined() => {
let [x, y, z, w] = to_vec::<f16, 4>(self.unpack_value(value)?);
sdf::Value::Vec4h(gf::vec4h(x, y, z, w))
}
Type::Vec4f if value.is_inlined() => {
let [x, y, z, w] = to_vec::<f32, 4>(self.unpack_value(value)?);
sdf::Value::Vec4f(gf::vec4f(x, y, z, w))
}
Type::Vec4d if value.is_inlined() => {
let [x, y, z, w] = to_vec::<f64, 4>(self.unpack_value(value)?);
sdf::Value::Vec4d(gf::vec4d(x, y, z, w))
}
Type::Vec4i if value.is_inlined() => {
let [x, y, z, w] = to_vec::<i32, 4>(self.unpack_value(value)?);
sdf::Value::Vec4i(gf::vec4i(x, y, z, w))
}
Type::Vec2h => sdf::Value::Vec2h(self.unpack_value::<gf::Vec2h>(value)?),
Type::Vec2f => sdf::Value::Vec2f(self.unpack_value::<gf::Vec2f>(value)?),
Type::Vec2d => sdf::Value::Vec2d(self.unpack_value::<gf::Vec2d>(value)?),
Type::Vec2i => sdf::Value::Vec2i(self.unpack_value::<gf::Vec2i>(value)?),
Type::Vec3h => sdf::Value::Vec3h(self.unpack_value::<gf::Vec3h>(value)?),
Type::Vec3f => sdf::Value::Vec3f(self.unpack_value::<gf::Vec3f>(value)?),
Type::Vec3d => sdf::Value::Vec3d(self.unpack_value::<gf::Vec3d>(value)?),
Type::Vec3i => sdf::Value::Vec3i(self.unpack_value::<gf::Vec3i>(value)?),
Type::Vec4h => sdf::Value::Vec4h(self.unpack_value::<gf::Vec4h>(value)?),
Type::Vec4f => sdf::Value::Vec4f(self.unpack_value::<gf::Vec4f>(value)?),
Type::Vec4d => sdf::Value::Vec4d(self.unpack_value::<gf::Vec4d>(value)?),
Type::Vec4i => sdf::Value::Vec4i(self.unpack_value::<gf::Vec4i>(value)?),
Type::Matrix2d if value.is_array() => Value::Matrix2dVec(
self.read_vec_array::<f64, 4>(value)?
.into_iter()
.map(gf::Mat2d)
.collect(),
),
Type::Matrix3d if value.is_array() => Value::Matrix3dVec(
self.read_vec_array::<f64, 9>(value)?
.into_iter()
.map(gf::Mat3d)
.collect(),
),
Type::Matrix4d if value.is_array() => Value::Matrix4dVec(
self.read_vec_array::<f64, 16>(value)?
.into_iter()
.map(gf::Matrix4d)
.collect(),
),
Type::Matrix2d if value.is_inlined() => {
sdf::Value::Matrix2d(gf::Mat2d(to_mat_diag::<2, 4>(self.unpack_value(value)?)))
}
Type::Matrix3d if value.is_inlined() => {
sdf::Value::Matrix3d(gf::Mat3d(to_mat_diag::<3, 9>(self.unpack_value(value)?)))
}
Type::Matrix4d if value.is_inlined() => {
sdf::Value::Matrix4d(gf::Matrix4d(to_mat_diag::<4, 16>(self.unpack_value(value)?)))
}
Type::Matrix2d => sdf::Value::Matrix2d(gf::Mat2d(self.unpack_value::<[f64; 4]>(value)?)),
Type::Matrix3d => sdf::Value::Matrix3d(gf::Mat3d(self.unpack_value::<[f64; 9]>(value)?)),
Type::Matrix4d => sdf::Value::Matrix4d(gf::Matrix4d(self.unpack_value::<[f64; 16]>(value)?)),
Type::Quath if value.is_array() => {
Value::QuathVec(xyzw_to_wxyz_quath(self.read_vec_array::<f16, 4>(value)?))
}
Type::Quath => {
let raw = self.unpack_value::<[f16; 4]>(value)?;
sdf::Value::quath(raw[3], raw[0], raw[1], raw[2])
}
Type::Quatf if value.is_array() => {
Value::QuatfVec(xyzw_to_wxyz_quatf(self.read_vec_array::<f32, 4>(value)?))
}
Type::Quatf => {
let raw = self.unpack_value::<[f32; 4]>(value)?;
sdf::Value::quatf(raw[3], raw[0], raw[1], raw[2])
}
Type::Quatd if value.is_array() => {
Value::QuatdVec(xyzw_to_wxyz_quatd(self.read_vec_array::<f64, 4>(value)?))
}
Type::Quatd => {
let raw = self.unpack_value::<[f64; 4]>(value)?;
sdf::Value::quatd(raw[3], raw[0], raw[1], raw[2])
}
Type::TokenListOp => {
corrupt!(!value.is_inlined());
let list = self.read_list_op(value, |file: &mut Self| {
Ok(file.read_token_vec()?.into_iter().map(tf::Token::from).collect())
})?;
sdf::Value::TokenListOp(list)
}
Type::StringListOp => {
corrupt!(!value.is_inlined());
let list = self.read_list_op(value, |file: &mut Self| file.read_string_vec())?;
sdf::Value::StringListOp(list)
}
Type::PathListOp => {
corrupt!(!value.is_inlined());
let list = self.read_list_op(value, |file: &mut Self| file.read_path_vec())?;
sdf::Value::PathListOp(list)
}
Type::ReferenceListOp => {
corrupt!(!value.is_inlined());
let list = self.read_list_op(value, |file: &mut Self| {
let count = file.reader.read_count()?;
let mut vec = Vec::with_capacity(count);
for _ in 0..count {
let reference = file.read_reference()?;
vec.push(reference);
}
Ok(vec)
})?;
sdf::Value::ReferenceListOp(list)
}
Type::IntListOp => {
corrupt!(!value.is_inlined());
sdf::Value::IntListOp(self.read_list_op(value, |f: &mut Self| f.read_pod_vec())?)
}
Type::Int64ListOp => {
corrupt!(!value.is_inlined());
sdf::Value::Int64ListOp(self.read_list_op(value, |f: &mut Self| f.read_pod_vec())?)
}
Type::UIntListOp => {
corrupt!(!value.is_inlined());
sdf::Value::UIntListOp(self.read_list_op(value, |f: &mut Self| f.read_pod_vec())?)
}
Type::UInt64ListOp => {
corrupt!(!value.is_inlined());
sdf::Value::UInt64ListOp(self.read_list_op(value, |f: &mut Self| f.read_pod_vec())?)
}
Type::TokenVector => {
corrupt!(!value.is_inlined());
self.set_position(value.payload())?;
let tokens = self.read_token_vec()?;
sdf::Value::token_vec(tokens)
}
Type::PathVector => {
corrupt!(!value.is_inlined());
self.set_position(value.payload())?;
let paths = self.read_path_vec()?;
sdf::Value::PathVec(paths)
}
Type::Specifier => {
let tmp: i32 = self.unpack_value(value)?;
let specifier = sdf::Specifier::from_repr(tmp)
.ok_or_else(|| ReadError::corrupt(format!("Unable to parse SDF specifier: {tmp}")))?;
sdf::Value::Specifier(specifier)
}
Type::Permission => {
let tmp: i32 = self.unpack_value(value)?;
let permission = sdf::Permission::from_repr(tmp)
.ok_or_else(|| ReadError::corrupt(format!("Unable to parse permission: {tmp}")))?;
sdf::Value::Permission(permission)
}
Type::Variability => {
let tmp: i32 = self.unpack_value(value)?;
let variability = sdf::Variability::from_repr(tmp)
.ok_or_else(|| ReadError::corrupt(format!("Unable to parse variability: {tmp}")))?;
sdf::Value::Variability(variability)
}
Type::LayerOffsetVector => {
corrupt!(!value.is_inlined());
corrupt!(!value.is_array());
corrupt!(!value.is_compressed());
self.set_position(value.payload())?;
let count = self.reader.read_count()?;
let vec = self.reader.read_vec(count)?;
sdf::Value::LayerOffsetVec(vec)
}
Type::Payload => {
corrupt!(!value.is_inlined());
corrupt!(!value.is_array());
corrupt!(!value.is_compressed());
self.set_position(value.payload())?;
let payload = self.read_payload()?;
sdf::Value::Payload(payload)
}
Type::PayloadListOp => {
let list = self.read_list_op(value, |file: &mut Self| {
let count = file.reader.read_count()?;
let mut vec = Vec::with_capacity(count);
for _ in 0..count {
let payload = file.read_payload()?;
vec.push(payload);
}
Ok(vec)
})?;
sdf::Value::PayloadListOp(list)
}
Type::VariantSelectionMap => {
corrupt!(!value.is_inlined());
corrupt!(!value.is_array());
corrupt!(!value.is_compressed());
self.set_position(value.payload())?;
let count = self.reader.read_count()?;
let mut map = HashMap::with_capacity(count);
for _ in 0..count {
let key = self.read_string()?;
let value = self.read_string()?;
map.insert(key, value);
}
sdf::Value::VariantSelectionMap(map)
}
Type::TimeSamples => {
corrupt!(!value.is_inlined());
corrupt!(!value.is_compressed());
self.set_position(value.payload())?;
self.apply_recursive_offset()?;
let times_rep = self.reader.read_pod::<ValueRep>()?;
let ty = times_rep.ty()?;
corrupt!(
ty == Type::DoubleVector || (ty == Type::Double && times_rep.is_array()),
"Invalid time samples type: expected either double vector or double array"
);
let saved_position = self.reader.stream_position()?;
let times = self
.value(times_rep)?
.try_as_double_vec()
.ok_or_else(|| ReadError::corrupt("Failed to read time samples"))?;
self.set_position(saved_position)?;
self.apply_recursive_offset()?;
let count = self.reader.read_count()?;
corrupt!(count == times.len(), "Invalid time samples count");
let value_reps = self.reader.read_vec::<ValueRep>(count)?;
debug_assert_eq!(value_reps.len(), count);
let samples = times
.into_iter()
.zip(value_reps)
.map(|(time, rep)| Ok((time, self.value(rep)?)))
.collect::<Result<Vec<_>, ReadError>>()?;
sdf::Value::TimeSamples(sdf::normalize_time_samples(samples))
}
Type::Dictionary if value.is_inlined() => sdf::Value::Dictionary(HashMap::default()),
Type::Dictionary => {
corrupt!(!value.is_compressed(), "Dictionary {ty} can't be compressed");
corrupt!(!value.is_array(), "Dictionary {ty} can't be inlined");
self.set_position(value.payload())?;
sdf::Value::Dictionary(self.read_custom_data()?)
}
Type::ValueBlock => sdf::Value::ValueBlock,
Type::Value => sdf::Value::Value,
Type::TimeCode if value.is_array() => sdf::Value::TimeCodeVec(
self.read_floats::<f64>(value)?
.into_iter()
.map(sdf::TimeCode::from)
.collect(),
),
Type::TimeCode => sdf::Value::TimeCode(self.unpack_value::<f64>(value)?.into()),
Type::PathExpression if value.is_array() => {
self.set_position(value.payload())?;
sdf::Value::PathExpressionVec(
self.read_string_vec()?
.iter()
.map(|text| sdf::PathExpression::parse(text))
.collect(),
)
}
Type::PathExpression => {
let expr = self.read_asset_path(value)?;
sdf::Value::PathExpression(sdf::PathExpression::parse(&expr))
}
Type::UnregisteredValue => {
let token = self.read_token(value)?;
sdf::Value::UnregisteredValue(token)
}
Type::UnregisteredValueListOp => {
corrupt!(!value.is_inlined());
let list = self.read_list_op(value, |file: &mut Self| file.read_string_vec())?;
sdf::Value::UnregisteredValueListOp(list)
}
Type::Relocates => {
corrupt!(!value.is_inlined());
self.set_position(value.payload())?;
let count = self.reader.read_count()?;
let mut pairs = Vec::with_capacity(count);
for _ in 0..count {
let src_idx: u32 = self.reader.read_pod()?;
let tgt_idx: u32 = self.reader.read_pod()?;
let src = indexed(&self.paths, src_idx as usize, "path")?.clone();
let tgt = indexed(&self.paths, tgt_idx as usize, "path")?.clone();
pairs.push((src, tgt));
}
sdf::Value::Relocates(pairs)
}
Type::Invalid => return Err(ReadError::unsupported(format!("Unsupported value type: {ty}"))),
};
Ok(variant)
}
}
enum ArrayKind {
Ints,
#[allow(dead_code)]
Floats,
Other,
}
fn indexed<'a, T>(table: &'a [T], index: usize, what: &'static str) -> Result<&'a T, ReadError> {
table
.get(index)
.ok_or_else(|| ReadError::corrupt(format!("{what} index {index} out of range ({} entries)", table.len())))
}
fn checked_path_slot(paths: &[sdf::Path], slot: usize) -> Result<(), ReadError> {
indexed(paths, slot, "path")?;
Ok(())
}
fn xyzw_to_wxyz_quatf(v: Vec<[f32; 4]>) -> Vec<gf::Quatf> {
v.into_iter().map(|q| gf::quatf(q[3], q[0], q[1], q[2])).collect()
}
fn xyzw_to_wxyz_quatd(v: Vec<[f64; 4]>) -> Vec<gf::Quatd> {
v.into_iter().map(|q| gf::quatd(q[3], q[0], q[1], q[2])).collect()
}
fn xyzw_to_wxyz_quath(v: Vec<[f16; 4]>) -> Vec<gf::Quath> {
v.into_iter().map(|q| gf::quath(q[3], q[0], q[1], q[2])).collect()
}
fn to_vec<T: From<i8>, const N: usize>(data: [i8; N]) -> [T; N] {
data.map(T::from)
}
fn to_mat_diag<const N: usize, const M: usize>(data: [i8; N]) -> [f64; M] {
let mut matrix = [0_f64; M];
for i in 0..N {
matrix[i * N + i] = data[i] as f64;
}
matrix
}
fn decompress_lz4(mut input: &[u8], output: &mut [u8]) -> Result<usize, ReadError> {
let chunks = input.read_pod::<u8>().ctx("lz4 chunk count")? as usize;
if chunks == 0 {
let size = lz4_flex::decompress_into(input, output)?;
Ok(size)
} else {
todo!("Support lz4 chunked decompression")
}
}
pub trait ReadExt {
fn read_count(&mut self) -> Result<usize, ReadError>;
fn read_pod<T: Default + Pod>(&mut self) -> Result<T, ReadError>;
fn read_vec<T: Default + NoUninit + AnyBitPattern>(&mut self, count: usize) -> Result<Vec<T>, ReadError>;
}
impl<R: io::Read> ReadExt for R {
fn read_count(&mut self) -> Result<usize, ReadError> {
let mut count = 0_u64;
self.read_exact(bytes_of_mut(&mut count)).ctx("size from IO stream")?;
Ok(count as usize)
}
fn read_pod<T: Default + Pod>(&mut self) -> Result<T, ReadError> {
let mut object = T::default();
self.read_exact(bytes_of_mut(&mut object))
.map_err(|error| ReadError::from(error).in_context(format!("pod {}", type_name::<T>())))?;
Ok(object)
}
fn read_vec<T: Default + NoUninit + AnyBitPattern>(&mut self, count: usize) -> Result<Vec<T>, ReadError> {
if count == 0 {
return Ok(Vec::new());
}
let mut vec = vec![T::default(); count];
self.read_exact(cast_slice_mut(&mut vec)).ctx("vec")?;
Ok(vec)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Result;
use std::fs;
#[test]
fn integer_compressed_float_fixture_uses_i_encoding() -> Result<()> {
let mut file = CrateFile::open(fs::File::open("fixtures/integer_compressed_floats.usdc")?)?;
let value = file
.fields
.iter()
.find_map(|field| {
let ty = field.value_rep.ty().ok()?;
(file.tokens[field.token_index] == "default"
&& ty == Type::Float
&& field.value_rep.is_array()
&& field.value_rep.is_compressed())
.then_some(field.value_rep)
})
.expect("fixture must contain one compressed float default value");
file.set_position(value.payload())?;
let (count, compressed) = file.unpack_array_len(value, ArrayKind::Floats)?;
assert_eq!(count, 16);
assert!(compressed);
assert_eq!(file.reader.read_pod::<u8>()?, b'i');
Ok(())
}
#[test]
fn test_read_crate_struct() {
let path = concat!(
env!("CARGO_WORKSPACE_DIR"),
"vendor/usd-wg-assets/full_assets/ElephantWithMonochord/SoC-ElephantWithMonochord.usdc"
);
if fs::metadata(path).is_err() {
eprintln!("Skipping test_read_crate_struct: fixture not available at {path}");
return;
}
let mut f = fs::File::open(path).expect("Failed to read crate file");
let file = CrateFile::open(&mut f).expect("Failed to read crate file");
assert_eq!(file.sections.len(), 6);
file.sections.iter().for_each(|section| {
assert!(!section.name().is_empty());
assert_ne!(section.start, 0_u64);
assert_ne!(section.size, 0_u64);
});
assert_eq!(file.tokens.len(), 192);
assert_eq!(file.fields.len(), 158);
file.fields.iter().for_each(|field| {
let _ = field.value_rep.ty().unwrap();
let _ = file.tokens[field.token_index];
});
assert_eq!(file.fieldsets.len(), 577);
assert_eq!(file.paths.len(), 248);
assert_eq!(file.specs.len(), 248);
assert!(file.validate().is_ok());
}
}