mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-14 06:22:24 +00:00
* Add craft-aep: After Effects project container and typed view from EffectCraft The .aep (RIFX 'Egg!') and .aepx container as a lossless chunk tree that writes back byte for byte, and the typed, read-only view over it: compositions, layers, properties, keyframes, effects and their settings data, masks, shapes, text documents, markers, cameras, track mattes, footage, project settings and the saving version. Moved from effectcraft crates/aep/src/container at f6b4da0a with its unit tests, the container integration tests, the aepdump and stubicc tools and docs/aep-format.md. The match-name tables (effect_map, style_map), the importer and the exporter are written against EffectCraft's model and stay there. ExtractorChannels::sources takes the caller's name for a copied channel instead of an EffectCraft constant. * craft-aep: read the Photoshop layer a footage item shows A footage item made from one layer of a .psd stores the layer in its `opti` (tag `8BPS`): the layer record's index (big-endian i32 at 14, -1 for the merged image), the document's two sides (little-endian u32 at 32 and 36), the layer's bounds (little-endian i32 at 78, 82, 86, 90) and its name (UTF-8 from 344). `Footage::psd_layer` reads them as `PsdLayer`. The format part of storytold/effectcraft#3449; the importer's use of it stays in EffectCraft. --------- Co-authored-by: arnav-hiwarkar <asharnav2008@gmail.com>
51 lines
1.6 KiB
Rust
51 lines
1.6 KiB
Rust
//! Bounds-checked big-endian readers shared by the typed views. Every read returns `None` when the
|
|
//! bytes are not there; nothing here indexes a slice directly.
|
|
|
|
pub(crate) fn text(b: &[u8]) -> String {
|
|
String::from_utf8_lossy(b).into_owned()
|
|
}
|
|
|
|
/// The bytes up to the first NUL (all of them when there is none).
|
|
pub(crate) fn until_nul(b: &[u8]) -> &[u8] {
|
|
let end = b.iter().position(|c| *c == 0).unwrap_or(b.len());
|
|
b.get(..end).unwrap_or(&[])
|
|
}
|
|
|
|
/// A NUL-terminated ASCII (or Latin-1) string at `at`, at most `max` bytes long.
|
|
pub(crate) fn cstr(b: &[u8], at: usize, max: usize) -> Option<String> {
|
|
let end = at.checked_add(max)?.min(b.len());
|
|
let field = b.get(at..end)?;
|
|
Some(until_nul(field).iter().map(|c| char::from(*c)).collect())
|
|
}
|
|
|
|
pub(crate) fn array<const N: usize>(b: &[u8], at: usize) -> Option<[u8; N]> {
|
|
let s = b.get(at..at.checked_add(N)?)?;
|
|
let mut a = [0u8; N];
|
|
a.copy_from_slice(s);
|
|
Some(a)
|
|
}
|
|
|
|
pub(crate) fn be_u16(b: &[u8], at: usize) -> Option<u16> {
|
|
array::<2>(b, at).map(u16::from_be_bytes)
|
|
}
|
|
|
|
pub(crate) fn be_u32(b: &[u8], at: usize) -> Option<u32> {
|
|
array::<4>(b, at).map(u32::from_be_bytes)
|
|
}
|
|
|
|
pub(crate) fn be_i32(b: &[u8], at: usize) -> Option<i32> {
|
|
array::<4>(b, at).map(i32::from_be_bytes)
|
|
}
|
|
|
|
pub(crate) fn le_u32(b: &[u8], at: usize) -> Option<u32> {
|
|
array::<4>(b, at).map(u32::from_le_bytes)
|
|
}
|
|
|
|
pub(crate) fn be_f32(b: &[u8], at: usize) -> Option<f32> {
|
|
array::<4>(b, at).map(f32::from_be_bytes)
|
|
}
|
|
|
|
pub(crate) fn be_f64(b: &[u8], at: usize) -> Option<f64> {
|
|
array::<8>(b, at).map(f64::from_be_bytes)
|
|
}
|