Files
craft-libs/crates/aep/src/error.rs
T
Dylan M. Taylorandarnav-hiwarkar 119ac0771f Add craft-aep: After Effects project container and typed view from EffectCraft (#888)
* 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>
2026-11-11 16:42:18 -05:00

58 lines
2.7 KiB
Rust

//! Errors of the container reader and writer.
use std::fmt;
/// Why a file could not be read or written.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AepError {
/// The input is shorter than the 12-byte `RIFX` header, or does not start with `RIFX`.
NotRifx,
/// The form type after the declared size is not `Egg!`.
BadFormType([u8; 4]),
/// The declared container size runs past the end of the input.
Truncated { declared: u64, available: u64 },
/// A chunk declares more body bytes than remain in its parent (`offset` is the chunk header).
ChunkTooLarge { offset: usize, size: u64, available: u64 },
/// The chunk structure is not well formed at `offset` (what is wrong is in `what`).
Malformed { offset: usize, what: &'static str },
/// Nesting is deeper than the allowed maximum.
TooDeep,
/// More chunks than the allowed maximum.
TooManyChunks,
/// A chunk or the whole file would not fit the 32-bit size fields when writing.
TooLarge,
/// An `.aepx` is not well-formed XML of the subset After Effects writes, at byte `offset`
/// (what is wrong is in `what`).
Xml { offset: usize, what: &'static str },
}
impl AepError {
/// Structural problems of the input that a lenient caller may repair by keeping a body opaque;
/// limits (`TooDeep`, `TooManyChunks`) are never repaired.
pub(crate) fn is_structural(&self) -> bool {
matches!(self, Self::ChunkTooLarge { .. } | Self::Malformed { .. } | Self::Truncated { .. })
}
}
impl fmt::Display for AepError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotRifx => write!(f, "not an After Effects project: no RIFX header"),
Self::BadFormType(t) => write!(f, "RIFX form type {:?} is not \"Egg!\"", String::from_utf8_lossy(t)),
Self::Truncated { declared, available } => {
write!(f, "truncated file: the container declares {declared} bytes, {available} are available")
}
Self::ChunkTooLarge { offset, size, available } => {
write!(f, "chunk at offset {offset} declares {size} bytes, only {available} remain")
}
Self::Malformed { offset, what } => write!(f, "malformed chunk structure at offset {offset}: {what}"),
Self::TooDeep => write!(f, "chunks are nested too deeply"),
Self::TooManyChunks => write!(f, "too many chunks"),
Self::TooLarge => write!(f, "chunk too large for the 32-bit size field"),
Self::Xml { offset, what } => write!(f, "not a readable After Effects XML project (.aepx): {what} at byte {offset}"),
}
}
}
impl std::error::Error for AepError {}