rakata_formats/lip/
mod.rs1mod reader;
31mod writer;
32
33pub use reader::{read_lip, read_lip_from_bytes};
34pub use writer::{write_lip, write_lip_to_vec};
35
36use num_enum::{IntoPrimitive, TryFromPrimitive};
37use thiserror::Error;
38
39use crate::binary::{self, DecodeBinary, EncodeBinary};
40
41const FILE_HEADER_SIZE: usize = 16;
43const KEYFRAME_ENTRY_SIZE: usize = 5;
45const LIP_MAGIC: [u8; 4] = *b"LIP ";
47const LIP_VERSION_V10: [u8; 4] = *b"V1.0";
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
52#[repr(u8)]
53pub enum LipShape {
54 Neutral = 0,
56 Ee = 1,
58 Eh = 2,
60 Ah = 3,
62 Oh = 4,
64 Ooh = 5,
66 Y = 6,
68 Sts = 7,
70 Fv = 8,
72 Ng = 9,
74 Th = 10,
76 Mpb = 11,
78 Td = 12,
80 Sh = 13,
82 L = 14,
84 Kg = 15,
86}
87
88impl LipShape {
89 pub fn from_raw_id(raw_id: u8) -> Option<Self> {
91 Self::try_from(raw_id).ok()
92 }
93
94 pub fn raw_id(self) -> u8 {
96 u8::from(self)
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
105pub struct LipShapeCode(u8);
106
107impl LipShapeCode {
108 pub const fn from_raw_id(raw_id: u8) -> Self {
110 Self(raw_id)
111 }
112
113 pub const fn raw_id(self) -> u8 {
115 self.0
116 }
117
118 pub fn known_shape(self) -> Option<LipShape> {
120 LipShape::from_raw_id(self.0)
121 }
122
123 pub fn is_known(self) -> bool {
125 self.known_shape().is_some()
126 }
127}
128
129impl From<LipShape> for LipShapeCode {
130 fn from(value: LipShape) -> Self {
131 Self(u8::from(value))
132 }
133}
134
135impl From<u8> for LipShapeCode {
136 fn from(value: u8) -> Self {
137 Self::from_raw_id(value)
138 }
139}
140
141#[derive(Debug, Clone, PartialEq)]
143pub struct LipKeyframe {
144 pub time: f32,
146 pub shape: LipShapeCode,
148}
149
150#[derive(Debug, Clone, PartialEq, Default)]
152pub struct Lip {
153 pub length: f32,
155 pub keyframes: Vec<LipKeyframe>,
157}
158
159impl Lip {
160 pub fn new() -> Self {
162 Self::default()
163 }
164
165 pub fn push_keyframe(&mut self, time: f32, shape_id: u8) {
167 self.push_keyframe_with_shape(time, LipShapeCode::from_raw_id(shape_id));
168 }
169
170 pub fn push_keyframe_with_shape(&mut self, time: f32, shape: LipShapeCode) {
172 self.keyframes.push(LipKeyframe { time, shape });
173 }
174}
175
176impl DecodeBinary for Lip {
177 type Error = LipBinaryError;
178
179 fn decode_binary(bytes: &[u8]) -> Result<Self, Self::Error> {
180 read_lip_from_bytes(bytes)
181 }
182}
183
184impl EncodeBinary for Lip {
185 type Error = LipBinaryError;
186
187 fn encode_binary(&self) -> Result<Vec<u8>, Self::Error> {
188 write_lip_to_vec(self)
189 }
190}
191
192#[derive(Debug, Error)]
194pub enum LipBinaryError {
195 #[error(transparent)]
197 Io(#[from] std::io::Error),
198 #[error("invalid LIP magic: {0:?}")]
200 InvalidMagic([u8; 4]),
201 #[error("invalid LIP version: {0:?}")]
203 InvalidVersion([u8; 4]),
204 #[error("invalid LIP header: {0}")]
206 InvalidHeader(String),
207 #[error("invalid LIP data: {0}")]
209 InvalidData(String),
210 #[error("value overflow while writing field `{0}`")]
212 ValueOverflow(&'static str),
213}
214
215impl From<binary::BinaryLayoutError> for LipBinaryError {
216 fn from(error: binary::BinaryLayoutError) -> Self {
217 Self::InvalidHeader(error.to_string())
218 }
219}