Skip to content

LIP File Format

Benjamin Auquite edited this page Nov 22, 2025 · 1 revision

KotOR LIP File Format Documentation

LIP (Lip Synchronization) files drive mouth animation for voiced dialogue. Each file contains a compact series of keyframes that map timestamps to discrete viseme (mouth shape) indices so that the engine can interpolate character lip movement while playing the companion WAV line.

Table of Contents


File Structure Overview

  • LIP files are always binary ("LIP V1.0" signature) and contain only animation data.
  • They are paired with WAV voice-over resources of identical duration; the LIP length field must match the WAV data playback time for glitch-free animation.
  • Keyframes are sorted chronologically and store a timestamp (float seconds) plus a 1-byte viseme index (0–15).
  • The layout is identical across vendor/reone, vendor/xoreos, vendor/Kotor.NET, vendor/KotOR.js, and vendor/mdlops, so the header/keyframe offsets below are cross-confirmed against those implementations.

Implementation: Libraries/PyKotor/src/pykotor/resource/formats/lip/


Binary Format

Header

Name Type Offset Size Description
File Type char[4] 0x00 4 Always "LIP "
File Version char[4] 0x04 4 Always "V1.0"
Sound Length float32 0x08 4 Duration in seconds (must equal WAV length)
Entry Count uint32 0x0C 4 Number of keyframes immediately following

Reference: vendor/reone/src/libs/graphics/format/lipreader.cpp:27-42

Keyframe Table

Keyframes follow immediately after the header; there is no padding.

Name Type Offset (per entry) Size Description
Timestamp float32 0x00 4 Seconds from animation start
Shape uint8 0x04 1 Viseme index (0–15)
  • Entries are stored sequentially and must be sorted ascending by timestamp.
  • Libraries average multiple implementations to validate this layout (vendor/reone, vendor/xoreos, vendor/KotOR.js, vendor/Kotor.NET).

Reference: vendor/KotOR.js/src/resource/LIPObject.ts:93-146


Mouth Shapes (Viseme Table)

KotOR reuses the 16-shape Preston Blair phoneme set. Every implementation agrees on the byte value assignments; KotOR.js only renames a few labels but the indices match.

Value Shape Description
0 NEUTRAL Rest/closed mouth
1 EE Teeth apart, wide smile (long “ee”)
2 EH Relaxed mouth (“eh”)
3 AH Mouth open (“ah/aa”)
4 OH Rounded lips (“oh”)
5 OOH Pursed lips (“oo”, “w”)
6 Y Slight smile (“y”)
7 STS Teeth touching (“s”, “z”, “ts”)
8 FV Lower lip touches teeth (“f”, “v”)
9 NG Tongue raised (“n”, “ng”)
10 TH Tongue between teeth (“th”)
11 MPB Lips closed (“m”, “p”, “b”)
12 TD Tongue up (“t”, “d”)
13 SH Rounded relaxed (“sh”, “ch”, “j”)
14 L Tongue forward (“l”, “r”)
15 KG Back of tongue raised (“k”, “g”, “h”)

Reference: Libraries/PyKotor/src/pykotor/resource/formats/lip/lip_data.py:50-169


Animation Rules


Implementation Details

The references above implement the same header layout and keyframe encoding, ensuring PyKotor stays compatible with the other toolchains.

Clone this wiki locally