rakata_formats/lip/
mod.rs1mod reader;
20mod writer;
21
22pub use reader::{read_lip, read_lip_from_bytes};
23pub use writer::{write_lip, write_lip_to_vec};
24
25use num_enum::{IntoPrimitive, TryFromPrimitive};
26use thiserror::Error;
27
28use crate::binary::{self, DecodeBinary, EncodeBinary};
29
30const FILE_HEADER_SIZE: usize = 16;
32const KEYFRAME_ENTRY_SIZE: usize = 5;
34const LIP_MAGIC: [u8; 4] = *b"LIP ";
36const LIP_VERSION_V10: [u8; 4] = *b"V1.0";
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
41#[repr(u8)]
42pub enum LipShape {
43 Neutral = 0,
45 Ee = 1,
47 Eh = 2,
49 Ah = 3,
51 Oh = 4,
53 Ooh = 5,
55 Y = 6,
57 Sts = 7,
59 Fv = 8,
61 Ng = 9,
63 Th = 10,
65 Mpb = 11,
67 Td = 12,
69 Sh = 13,
71 L = 14,
73 Kg = 15,
75}
76
77impl LipShape {
78 pub fn from_raw_id(raw_id: u8) -> Option<Self> {
80 Self::try_from(raw_id).ok()
81 }
82
83 pub fn raw_id(self) -> u8 {
85 u8::from(self)
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
94pub struct LipShapeCode(u8);
95
96impl LipShapeCode {
97 pub const fn from_raw_id(raw_id: u8) -> Self {
99 Self(raw_id)
100 }
101
102 pub const fn raw_id(self) -> u8 {
104 self.0
105 }
106
107 pub fn known_shape(self) -> Option<LipShape> {
109 LipShape::from_raw_id(self.0)
110 }
111
112 pub fn is_known(self) -> bool {
114 self.known_shape().is_some()
115 }
116}
117
118impl From<LipShape> for LipShapeCode {
119 fn from(value: LipShape) -> Self {
120 Self(u8::from(value))
121 }
122}
123
124impl From<u8> for LipShapeCode {
125 fn from(value: u8) -> Self {
126 Self::from_raw_id(value)
127 }
128}
129
130#[derive(Debug, Clone, PartialEq)]
132pub struct LipKeyframe {
133 pub time: f32,
135 pub shape: LipShapeCode,
137}
138
139#[derive(Debug, Clone, PartialEq, Default)]
141pub struct Lip {
142 pub length: f32,
144 pub keyframes: Vec<LipKeyframe>,
146}
147
148impl Lip {
149 pub fn new() -> Self {
151 Self::default()
152 }
153
154 pub fn push_keyframe(&mut self, time: f32, shape_id: u8) {
156 self.push_keyframe_with_shape(time, LipShapeCode::from_raw_id(shape_id));
157 }
158
159 pub fn push_keyframe_with_shape(&mut self, time: f32, shape: LipShapeCode) {
161 self.keyframes.push(LipKeyframe { time, shape });
162 }
163}
164
165impl DecodeBinary for Lip {
166 type Error = LipBinaryError;
167
168 fn decode_binary(bytes: &[u8]) -> Result<Self, Self::Error> {
169 read_lip_from_bytes(bytes)
170 }
171}
172
173impl EncodeBinary for Lip {
174 type Error = LipBinaryError;
175
176 fn encode_binary(&self) -> Result<Vec<u8>, Self::Error> {
177 write_lip_to_vec(self)
178 }
179}
180
181#[derive(Debug, Error)]
183pub enum LipBinaryError {
184 #[error(transparent)]
186 Io(#[from] std::io::Error),
187 #[error("invalid LIP magic: {0:?}")]
189 InvalidMagic([u8; 4]),
190 #[error("invalid LIP version: {0:?}")]
192 InvalidVersion([u8; 4]),
193 #[error("invalid LIP header: {0}")]
195 InvalidHeader(String),
196 #[error("invalid LIP data: {0}")]
198 InvalidData(String),
199 #[error("value overflow while writing field `{0}`")]
201 ValueOverflow(&'static str),
202}
203
204impl From<binary::BinaryLayoutError> for LipBinaryError {
205 fn from(error: binary::BinaryLayoutError) -> Self {
206 Self::InvalidHeader(error.to_string())
207 }
208}