Skip to content

Parsing Boolean Flags (1-bit attributes)

This guide walks you through the process of adding support for a new boolean attribute from an After Effects project file. Boolean flags are typically stored as individual bits within a byte, so we need to identify which bit corresponds to which attribute.

Overview

The workflow for adding a new boolean flag:

  1. Compare files - Create two .aep files that differ only in the target attribute
  2. Find the byte - Locate the byte that changes between the two files
  3. Identify the bit - Determine which bit within that byte represents the flag
  4. Update Kaitai schema - Add the bit field to aep.ksy
  5. Regenerate parser - Compile the updated schema to Python
  6. Update model - Add the attribute to the appropriate dataclass
  7. Update parser logic - Wire the parsed data to the model
  8. Add tests - Validate your changes with test cases

Detailed Steps

1. Create Test Files

Create two minimal .aep files that differ only in the boolean attribute you want to parse:

  1. Open After Effects
  2. Create a minimal project (e.g., a single composition with one layer)
  3. Save as test_false.aep
  4. Change only the boolean attribute you want to parse (e.g., toggle "Motion Blur" on a layer)
  5. Save as test_true.aep

Tip: Also save as .aepx format - it's XML-based and easier to read when debugging.

2. Compare Files and Find the Byte

Use the aep-compare CLI tool to find differences:

# Compare the two files
aep-compare test_false.aep test_true.aep

# Filter by specific chunk type if you know it
aep-compare test_false.aep test_true.aep --filter ldta

Look for bytes that change. For example, you might see: - cdta chunk, byte at position 42: 1014

3. Identify the Bit Position

Convert the byte values to binary to identify which bit changed:

10 (decimal) = 00001010 (binary)
14 (decimal) = 00001110 (binary)
                    ^^
                    bit 2 changed from 0 to 1

Bit numbering convention (right to left):

Bit:  7 6 5 4 3 2 1 0
      0 0 0 0 1 0 1 0  = 10
      0 0 0 0 1 1 1 0  = 14

In this example, bit 2 represents our boolean flag.

Tip: Use the Kaitai Struct Web IDE to visualize the binary structure and confirm the bit position.

4. Update the Kaitai Schema

Open src/aep_parser/kaitai/aep.ksy and find the chunk definition (e.g., cdta). Add bit fields for the byte:

# Before (reading whole byte)
- id: flags
  type: u1  # 8-bit unsigned integer

# After (reading individual bits)
- id: preserve_nested_resolution
  type: b1  # bit 7
- type: b1  # skip bit 6
- id: preserve_nested_frame_rate
  type: b1  # bit 5
- id: frame_blending
  type: b1  # bit 4
- id: some_existing_flag
  type: b1  # bit 3
- id: motion_blur  # <-- Add this (matches bit 2 from example above)
  type: b1  # bit 2
- type: b2  # skip bits 1-0

Important notes: - Bits are read from most significant to least significant (left to right: 7→0) - Use b1 for single bits, b2 for two bits, etc. - Skip unknown bits with type: b1 (no id) - All bits in a byte must be accounted for (they must sum to 8)

5. Regenerate the Kaitai Parser

Compile the updated schema to Python:

kaitai-struct-compiler --target python \
  --outdir src/aep_parser/kaitai \
  src/aep_parser/kaitai/aep.ksy

The compiler generates src/aep_parser/kaitai/aep.py automatically. Do not edit this file manually.

6. Update the Model Dataclass

Add the attribute to the appropriate model class. Check the After Effects Scripting Guide to find: - The correct attribute name (e.g., motionBlur) - Which class it belongs to (e.g., AVLayer) - The description for your docstring

Example in src/aep_parser/models/layers/av_layer.py:

@dataclass
class AVLayer(Layer):
    """AV Layer with visual and audio properties.

    Corresponds to After Effects' AVLayer object.
    """

    # ... other fields ...

    motion_blur: bool
    """Whether motion blur is enabled for this layer.

    Corresponds to AVLayer.motionBlur in ExtendScript.
    """

Naming convention: Use snake_case for Python attributes, even if the AE scripting API uses camelCase.

7. Update the Parser Logic

Wire the parsed bit to the model in the corresponding parser (e.g., src/aep_parser/parsers/layer.py):

def parse_av_layer(chunk: Aep.Chunk, context: Context) -> AVLayer:
    """Parse an AV layer from layer chunks."""

    # ... existing parsing code ...

    # Get the flags chunk
    flags_chunk = find_by_type(chunks=child_chunks, chunk_type="cdta")

    return AVLayer(
        # ... other arguments ...
        motion_blur=flags_chunk.data.motion_blur,  # <-- Add this
    )

8. Add Tests

Create a test case to validate your new attribute:

  1. Create a minimal .aep file with the attribute enabled (samples/models/layers/motion_blur_enabled.aep)
  2. Optional: Use the JSX scripts to generate test samples and validation data:
  3. Run scripts/jsx/export_project_json.jsx in After Effects to export the project as JSON for validation
  4. Update scripts/jsx/generate_model_samples.jsx if you're adding a new attribute that should be systematically tested
  5. Add a test in tests/test_models_layer.py:
def test_layer_motion_blur():
    """Test parsing motion blur flag from layer."""
    project = parse_project("samples/models/layers/motion_blur_enabled.aep")
    comp = project.items[0]
    layer = comp.layers[0]

    assert layer.motion_blur is True

Run the test:

pytest tests/test_models_layer.py::test_layer_motion_blur -v

Tips and Best Practices

  • Start simple: Test with minimal .aep files to reduce complexity
  • Use .aepx format: XML is easier to read than binary when debugging
  • Compare one change at a time: Only toggle one attribute between test files
  • Check the scripting guide: Always reference the After Effects Scripting Guide for correct naming and types
  • Use the Web IDE: The Kaitai Struct Web IDE is invaluable for visualizing binary structures
  • Test thoroughly: Add test cases for both true and false values

Common Pitfalls

  • Wrong bit order: Remember that bits are numbered right-to-left (7→0), but in the Kaitai schema you define them top-to-bottom (bit 7 first, then bit 6, etc.)
  • Missing bits: Every bit in a byte must be accounted for - use type: b1 to skip unknown bits
  • Wrong chunk: Make sure you're looking at the correct parent chunk (use aep-compare to verify)
  • Forgetting to regenerate: Always regenerate aep.py after editing aep.ksy

See Also