LIP (Lip Synching)
LIP files provide keyframed facial morph data directly bound to audio streams, instructing character models how to physically animate their mouths to match speech.
At a Glance
| Property | Value |
|---|---|
| Extension(s) | .lip |
| Magic Signature | LIP V1.0 |
| Type | Facial Animation Keyframes |
| Rust Reference | View rakata_formats::Lip in Rustdocs |
Data Model Structure
The rakata-formats crate maps LIP binaries into the Lip structure. It extracts the raw 5-byte sequential keyframe array and cleanly projects it into a format that pairs each chronological float timestamp directly with its localized mouth shape.
Structural Layout
| Offset | Type | Description |
|---|---|---|
0x00 | CHAR[8] | Signature (LIP V1.0) |
0x08 | FLOAT | Animation Length |
0x0C | DWORD | Entry Count |
0x10 | Struct[] | Keyframe Array (5 bytes per entry) |
Engine Audits & Decompilation
The following documents the engine’s exact load sequence for Lip Synching animations mapped from swkotor.exe.
(Decompilation logic for this section was audited and verified via native Ghidra pipeline against swkotor.exe, explicitly pulling from CLIP::LoadLip at 0x0070c590.)
| Pipeline Event | Ghidra Provenance & Engine Behavior |
|---|---|
| Zero-Copy Loading | The engine handles LIP files as completely flat structures. Instead of parsing the variables out individually, it simply verifies the "LIP V1.0" signature and pulls the animation length and entry count directly from offsets +0x08 and +0x0C. |
| Direct Array Assignment | The keyframes are packed into identical 5-byte chunks (a 4-byte float for the timestamp, and a 1-byte integer determining the mouth shape). Because of this flat layout, the engine never loops through the data to read it. It simply points its internal animations memory pointer perfectly to file offset +0x10 and natively runs the animation straight off the raw file buffer. |