1use crate::player::{ObjRef, Player};
18use crate::value::{Array, Value};
19use crate::{Error, Result, invalid};
20
21#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct GenericBinding {
24 pub path: u32,
26 pub attribute: u32,
29 pub type_id: i64,
31 pub is_pptr: bool,
33}
34
35impl GenericBinding {
36 pub fn width(&self) -> usize {
38 match (self.type_id, self.attribute) {
39 (4, 1 | 3 | 4) => 3,
40 (4, 2) => 4,
41 _ => 1,
42 }
43 }
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub enum Storage {
49 Streamed,
51 Dense,
53 Constant,
55}
56
57#[derive(Clone, Copy, Debug, PartialEq)]
60pub struct Key {
61 pub time: f32,
63 pub value: f32,
65 pub in_slope: f32,
67 pub out_slope: f32,
69}
70
71#[derive(Clone, Debug, PartialEq)]
74pub struct Curve {
75 pub storage: Storage,
77 pub keys: Vec<Key>,
79}
80
81impl Curve {
82 pub fn sample(&self, t: f32) -> (f32, f32, f32) {
86 let k = &self.keys;
87 let (Some(first), Some(last)) = (k.first(), k.last()) else {
88 return (0.0, 0.0, 0.0);
89 };
90 if t.is_nan() {
91 return (f32::NAN, 0.0, 0.0);
92 }
93 if t <= first.time {
94 return (
95 first.value,
96 0.0,
97 if t == first.time { first.out_slope } else { 0.0 },
98 );
99 }
100 if t >= last.time {
101 return (last.value, if t == last.time { last.in_slope } else { 0.0 }, 0.0);
102 }
103 let after = k.partition_point(|key| key.time <= t);
105 let (Some(a), Some(b)) = (after.checked_sub(1).and_then(|i| k.get(i)), k.get(after)) else {
106 return (last.value, 0.0, 0.0);
107 };
108 if t == a.time {
109 return (a.value, a.in_slope, a.out_slope);
110 }
111 if !a.out_slope.is_finite() {
112 return (a.value, 0.0, 0.0);
113 }
114 let (dt, s) = (
115 f64::from(b.time - a.time),
116 f64::from(t - a.time) / f64::from(b.time - a.time),
117 );
118 let (p0, p1) = (f64::from(a.value), f64::from(b.value));
119 let (m0, m1) = (f64::from(a.out_slope) * dt, f64::from(b.in_slope) * dt);
120 let (s2, s3) = (s * s, s * s * s);
121 let value = (2.0 * s3 - 3.0 * s2 + 1.0) * p0
122 + (s3 - 2.0 * s2 + s) * m0
123 + (-2.0 * s3 + 3.0 * s2) * p1
124 + (s3 - s2) * m1;
125 let slope = ((6.0 * s2 - 6.0 * s) * p0
126 + (3.0 * s2 - 4.0 * s + 1.0) * m0
127 + (-6.0 * s2 + 6.0 * s) * p1
128 + (3.0 * s2 - 2.0 * s) * m1)
129 / dt;
130 (crate::narrow(value), crate::narrow(slope), crate::narrow(slope))
131 }
132
133 pub fn has_steps(&self) -> bool {
135 self.keys.iter().any(|k| !k.out_slope.is_finite())
136 }
137
138 pub fn is_constant(&self) -> bool {
140 self.keys.len() == 1
141 }
142}
143
144#[derive(Clone, Debug, PartialEq)]
146pub struct Track {
147 pub binding: GenericBinding,
149 pub curves: Vec<Curve>,
151}
152
153#[derive(Clone, Debug, PartialEq)]
155pub struct ClipData {
156 pub name: String,
158 pub duration: f32,
160 pub looping: bool,
162 pub tracks: Vec<Track>,
164 pub events: usize,
166 pub worst_continuity_error: f32,
169}
170
171const KEY_WORDS: usize = 5;
173
174fn int(v: &Value, k: &str) -> Result<i64> {
175 v.get(k)
176 .and_then(Value::as_i64)
177 .ok_or_else(|| invalid(format!("clip: no integer {k}")))
178}
179
180fn float(v: &Value, k: &str) -> Result<f32> {
181 v.get(k)
182 .and_then(Value::as_f32)
183 .ok_or_else(|| invalid(format!("clip: no float {k}")))
184}
185
186fn list<'a>(v: &'a Value, k: &str) -> Result<&'a [Value]> {
187 match v.get(k) {
188 Some(Value::Array(Array::Values(items))) => Ok(items),
189 Some(Value::Array(a)) if a.is_empty() => Ok(&[]),
190 _ => Err(invalid(format!("clip: no list {k}"))),
191 }
192}
193
194fn floats(v: &Value, k: &str) -> Result<Vec<f32>> {
195 match v.get(k) {
196 Some(Value::Array(Array::F32(f))) => Ok(f.clone()),
197 Some(Value::Array(a)) if a.is_empty() => Ok(Vec::new()),
198 _ => Err(invalid(format!("clip: no float list {k}"))),
199 }
200}
201
202fn read_streamed(name: &str, words: &[u32], curve_count: usize) -> Result<(Vec<Curve>, f32)> {
205 let bad = |why: String| invalid(format!("{name}: streamed clip: {why}"));
206 let most = words.len() / KEY_WORDS;
207 if curve_count > most {
208 return Err(bad(format!(
209 "{curve_count} curves, but {} bytes of frames hold at most {most} keys, and every curve has one",
210 words.len().saturating_mul(4)
211 )));
212 }
213 let bytes: Vec<u8> = words.iter().flat_map(|w| w.to_le_bytes()).collect();
214 let mut entries: Vec<Vec<(f32, [f32; 4])>> = vec![Vec::new(); curve_count];
216 let mut at = 0;
217 let take = |at: &mut usize, n: usize| -> Result<&[u8]> {
218 let end = at.checked_add(n);
219 let part = end.and_then(|end| bytes.get(*at..end)).ok_or_else(|| {
220 bad(format!(
221 "frames past the data at byte {at} of {}",
222 bytes.len(),
223 at = *at
224 ))
225 })?;
226 *at = end.unwrap_or(*at);
227 Ok(part)
228 };
229 let mut last_time = f32::NEG_INFINITY;
230 let mut ended = false;
231 while at < bytes.len() {
232 let frame_at = at;
233 let time = f32::from_le_bytes(take(&mut at, 4)?.try_into().expect("4 bytes"));
234 let count = i32::from_le_bytes(take(&mut at, 4)?.try_into().expect("4 bytes"));
235 if time.is_nan() || time < last_time {
236 return Err(bad(format!(
237 "frame time {time} after {last_time} (the frame at byte {frame_at}; times must not decrease)"
238 )));
239 }
240 last_time = time;
241 let count = usize::try_from(count).map_err(|_| {
242 bad(format!(
243 "{count} keys in the frame at byte {frame_at} (at least 0)"
244 ))
245 })?;
246 for _ in 0..count {
247 let key_at = at;
248 let index = i32::from_le_bytes(take(&mut at, 4)?.try_into().expect("4 bytes"));
249 let mut c = [0.0f32; 4];
250 for x in &mut c {
251 *x = f32::from_le_bytes(take(&mut at, 4)?.try_into().expect("4 bytes"));
252 }
253 let slot = usize::try_from(index).ok().ok_or_else(|| {
254 bad(format!(
255 "a key of curve {index} of {curve_count} at byte {key_at}"
256 ))
257 })?;
258 entries
259 .get_mut(slot)
260 .ok_or_else(|| {
264 Error::Unsupported(format!(
265 "{name}: streamed clip: a key of curve {index} of {curve_count} at byte {key_at}, a curve \
266 past the stated count"
267 ))
268 })?
269 .push((time, c));
270 }
271 if time == f32::INFINITY {
272 ended = true;
273 break;
274 }
275 }
276 if !ended || at != bytes.len() {
277 return Err(bad(format!(
278 "the frames do not end with the +inf frame at the end of the data (frames end at byte {at} of {})",
279 bytes.len()
280 )));
281 }
282 let mut worst = 0.0f32;
283 let mut out = Vec::with_capacity(curve_count);
284 for (i, list) in entries.iter().enumerate() {
285 let real: Vec<&(f32, [f32; 4])> = list
287 .iter()
288 .filter(|(t, _)| *t > f32::MIN / 2.0 && t.is_finite())
289 .collect();
290 let mut keys: Vec<Key> = Vec::new();
291 if real.is_empty() {
292 let (_, c) = list.first().ok_or_else(|| bad(format!("curve {i} has no key")))?;
293 keys.push(Key {
294 time: 0.0,
295 value: c[3],
296 in_slope: 0.0,
297 out_slope: 0.0,
298 });
299 }
300 let mut previous: Option<(f32, [f32; 4])> = None;
301 for &&(time, c) in &real {
302 let in_slope = match previous {
303 Some((t0, p)) => {
304 let dt = time - t0;
305 let end = ((p[0] * dt + p[1]) * dt + p[2]) * dt + p[3];
308 if dt >= 1e-3 && end.is_finite() && c[3].is_finite() {
309 worst = worst.max((end - c[3]).abs() / (1.0 + c[3].abs()));
310 }
311 (3.0 * p[0] * dt + 2.0 * p[1]) * dt + p[2]
312 }
313 None => 0.0,
314 };
315 keys.push(Key {
316 time,
317 value: c[3],
318 in_slope,
319 out_slope: c[2],
320 });
321 previous = Some((time, c));
322 }
323 for k in &mut keys {
325 if !k.in_slope.is_finite() {
326 k.in_slope = f32::INFINITY;
327 }
328 }
329 out.push(Curve {
330 storage: Storage::Streamed,
331 keys,
332 });
333 }
334 Ok((out, worst))
335}
336
337fn read_dense(name: &str, dense: &Value) -> Result<Vec<Curve>> {
341 let bad = |why: String| invalid(format!("{name}: dense clip: {why}"));
342 let dense_curves =
343 usize::try_from(int(dense, "m_CurveCount")?).map_err(|_| bad("a negative curve count".into()))?;
344 let frames =
345 usize::try_from(int(dense, "m_FrameCount")?).map_err(|_| bad("a negative frame count".into()))?;
346 let samples = floats(dense, "m_SampleArray")?;
347 if frames.checked_mul(dense_curves) != Some(samples.len()) {
348 return Err(bad(format!(
349 "{} samples for {frames} frames of {dense_curves} curves",
350 samples.len()
351 )));
352 }
353 if frames == 0 && dense_curves > 0 {
354 return Err(bad(format!("{dense_curves} curves of no frames")));
355 }
356 let (rate, begin) = (float(dense, "m_SampleRate")?, float(dense, "m_BeginTime")?);
357 if frames > 1 && !(rate.is_finite() && rate > 0.0) {
358 return Err(bad(format!(
359 "sample rate {rate} for {frames} frames (a positive rate is needed)"
360 )));
361 }
362 let times: Vec<f32> = (0..frames).map(|f| begin + f as f32 / rate).collect();
363 let mut curves = Vec::with_capacity(dense_curves);
364 for c in 0..dense_curves {
365 let values: Vec<f32> = samples.iter().skip(c).step_by(dense_curves).copied().collect();
366 let slope = |a: usize, b: usize| match (values.get(a..=b), times.get(a..=b)) {
367 (Some([v0, .., v1]), Some([t0, .., t1])) => (v1 - v0) / (t1 - t0),
368 _ => 0.0,
369 };
370 curves.push(Curve {
371 storage: Storage::Dense,
372 keys: values
373 .iter()
374 .zip(×)
375 .enumerate()
376 .map(|(f, (&value, &time))| Key {
377 time,
378 value,
379 in_slope: f.checked_sub(1).map_or(0.0, |p| slope(p, f)),
380 out_slope: if f < frames.saturating_sub(1) {
381 slope(f, f.saturating_add(1))
382 } else {
383 0.0
384 },
385 })
386 .collect(),
387 });
388 }
389 Ok(curves)
390}
391
392impl ClipData {
393 pub fn read(player: &Player, r: &ObjRef) -> Result<ClipData> {
395 let c = player.decode(r)?;
396 let name = c.get("m_Name").and_then(Value::as_str).unwrap_or("").to_owned();
397 if c.get("m_Legacy")
398 .is_some_and(|l| matches!(l, Value::Bool(true)) || l.as_i64().is_some_and(|v| v != 0))
399 {
400 return Err(Error::Unsupported(format!("{name}: legacy animation clip")));
401 }
402 let muscle = c
403 .get("m_MuscleClip")
404 .ok_or_else(|| invalid(format!("{name}: no m_MuscleClip")))?;
405 let data = muscle
406 .at(&["m_Clip", "data"])
407 .ok_or_else(|| invalid(format!("{name}: no m_MuscleClip.m_Clip.data")))?;
408 let streamed = data
409 .get("m_StreamedClip")
410 .ok_or_else(|| invalid(format!("{name}: no m_StreamedClip")))?;
411 let dense = data
412 .get("m_DenseClip")
413 .ok_or_else(|| invalid(format!("{name}: no m_DenseClip")))?;
414 let constant = data
415 .get("m_ConstantClip")
416 .ok_or_else(|| invalid(format!("{name}: no m_ConstantClip")))?;
417 let start = float(muscle, "m_StartTime")?;
418 let stop = float(muscle, "m_StopTime")?;
419 if start != 0.0 {
420 return Err(Error::Unsupported(format!(
421 "{name}: a clip starting at {start} s"
422 )));
423 }
424 let streamed_words = match streamed.get("data") {
425 Some(Value::Array(Array::U32(w))) => w.as_slice(),
426 Some(Value::Array(a)) if a.is_empty() => &[],
427 _ => return Err(invalid(format!("{name}: streamed clip data is not 32-bit words"))),
428 };
429 let streamed_count = usize::try_from(int(streamed, "curveCount")?)
430 .map_err(|_| invalid(format!("{name}: negative curve count")))?;
431 let (mut curves, worst) = if streamed_count == 0 && streamed_words.is_empty() {
432 (Vec::new(), 0.0)
433 } else {
434 read_streamed(&name, streamed_words, streamed_count)?
435 };
436 curves.extend(read_dense(&name, dense)?);
437 for v in floats(constant, "data")? {
438 curves.push(Curve {
439 storage: Storage::Constant,
440 keys: vec![Key {
441 time: 0.0,
442 value: v,
443 in_slope: 0.0,
444 out_slope: 0.0,
445 }],
446 });
447 }
448 let mut bindings = Vec::new();
450 let constant_bindings = c
451 .get("m_ClipBindingConstant")
452 .ok_or_else(|| invalid(format!("{name}: no m_ClipBindingConstant")))?;
453 for b in list(constant_bindings, "genericBindings")? {
454 bindings.push(GenericBinding {
455 path: u32::try_from(int(b, "path")?)
456 .map_err(|_| invalid(format!("{name}: a path hash beyond 32 bits")))?,
457 attribute: u32::try_from(int(b, "attribute")?)
458 .map_err(|_| invalid(format!("{name}: an attribute beyond 32 bits")))?,
459 type_id: int(b, "typeID")?,
460 is_pptr: int(b, "isPPtrCurve")? != 0,
461 });
462 }
463 if let Some(b) = bindings.iter().find(|b| b.is_pptr) {
464 return Err(Error::Unsupported(format!(
465 "{name}: a PPtr curve (attribute {})",
466 b.attribute
467 )));
468 }
469 let wanted: usize = bindings.iter().map(GenericBinding::width).sum();
470 if wanted != curves.len() {
471 return Err(invalid(format!(
472 "{name}: the bindings take {wanted} curves, the clip stores {}",
473 curves.len()
474 )));
475 }
476 let mut rest = curves.into_iter();
477 let tracks = bindings
478 .into_iter()
479 .map(|binding| {
480 let curves = rest.by_ref().take(binding.width()).collect();
481 Track { binding, curves }
482 })
483 .collect();
484 let looping = muscle
485 .get("m_LoopTime")
486 .is_some_and(|l| matches!(l, Value::Bool(true)) || l.as_i64().is_some_and(|v| v != 0));
487 Ok(ClipData {
488 name,
489 duration: stop - start,
490 looping,
491 tracks,
492 events: list(&c, "m_Events")?.len(),
493 worst_continuity_error: worst,
494 })
495 }
496}
497
498pub fn path_hash(path: &str) -> u32 {
501 crate::animator::name_hash(path)
502}
503
504pub fn controller_clips(player: &Player, controller: &ObjRef) -> Result<Vec<ObjRef>> {
506 let c = player.decode(controller)?;
507 let mut out = Vec::new();
508 for p in list(&c, "m_AnimationClips")? {
509 if let Some(clip) = player.resolve(&controller.file, p)? {
510 out.push(clip);
511 }
512 }
513 Ok(out)
514}
515
516#[cfg(test)]
517mod tests {
518 use super::*;
519
520 fn key(time: f32, value: f32, in_slope: f32, out_slope: f32) -> Key {
521 Key {
522 time,
523 value,
524 in_slope,
525 out_slope,
526 }
527 }
528
529 fn frame(time: f32, keys: &[(i32, [f32; 4])]) -> Vec<u32> {
530 let mut words = vec![time.to_bits(), keys.len() as u32];
531 for (index, c) in keys {
532 words.push(*index as u32);
533 words.extend(c.iter().map(|x| x.to_bits()));
534 }
535 words
536 }
537
538 #[test]
541 fn a_streamed_curve_decodes_to_keys_with_slopes() {
542 let (c0, c1, c2, c3) = (0.5f32, -1.0, 3.0, 1.0);
544 let end = ((c0 * 2.0 + c1) * 2.0 + c2) * 2.0 + c3;
545 let mut words = frame(-f32::MAX, &[(0, [0.0, 0.0, 0.0, 1.0])]);
546 words.extend(frame(0.0, &[(0, [c0, c1, c2, c3])]));
547 words.extend(frame(2.0, &[(0, [0.0, 0.0, 0.5, end])]));
548 words.extend(frame(f32::INFINITY, &[]));
549 let (curves, worst) = read_streamed("t", &words, 1).unwrap();
550 assert_eq!(worst, 0.0);
551 let k = &curves[0].keys;
552 assert_eq!(k.len(), 2);
553 assert_eq!((k[0].time, k[0].value, k[0].out_slope), (0.0, 1.0, 3.0));
554 assert_eq!(
556 (k[1].time, k[1].value, k[1].in_slope, k[1].out_slope),
557 (2.0, end, 5.0, 0.5)
558 );
559 let (v, s, _) = curves[0].sample(1.0);
561 assert!((v - (0.5 - 1.0 + 3.0 + 1.0)).abs() < 1e-5, "{v}");
562 assert!((s - (1.5 - 2.0 + 3.0)).abs() < 1e-5, "{s}");
563 assert_eq!(curves[0].sample(0.0), (1.0, 0.0, 3.0));
564 assert_eq!(curves[0].sample(2.0), (end, 5.0, 0.0));
565 }
566
567 #[test]
570 fn streamed_clips_refuse_what_is_not_the_format() {
571 let mut words = frame(-f32::MAX, &[(0, [0.0, 0.0, 0.0, 4.0])]);
572 words.extend(frame(f32::INFINITY, &[]));
573 let (curves, _) = read_streamed("t", &words, 1).unwrap();
574 assert_eq!(curves[0].keys, vec![key(0.0, 4.0, 0.0, 0.0)]);
575 let refused = |words: &[u32], n: usize| read_streamed("t", words, n).unwrap_err().to_string();
576 assert!(refused(&words[..words.len() - 2], 1).contains("do not end"));
577 assert!(refused(&words, 0).contains("curve 0 of 0"));
578 let mut back = frame(1.0, &[]);
579 back.extend(frame(0.5, &[]));
580 back.extend(frame(f32::INFINITY, &[]));
581 assert!(refused(&back, 1).contains("after"));
582 }
583
584 #[test]
587 fn a_streamed_curve_count_is_bounded_by_the_data() {
588 let mut words = frame(-f32::MAX, &[(0, [0.0, 0.0, 0.0, 4.0])]);
589 words.extend(frame(f32::INFINITY, &[]));
590 for n in [2, 1 << 40, usize::MAX] {
591 let e = read_streamed("t", &words, n).unwrap_err().to_string();
592 assert!(e.contains("hold at most 1 keys"), "{e}");
593 }
594 let mut words = frame(-f32::MAX, &[(5, [0.0; 4])]);
596 words.extend(frame(f32::INFINITY, &[]));
597 let e = read_streamed("t", &words, 1).unwrap_err();
598 assert!(matches!(e, Error::Unsupported(_)), "{e}");
600 assert!(e.to_string().contains("curve 5 of 1 at byte 8"), "{e}");
601 let mut negative = frame(-f32::MAX, &[(-1, [0.0; 4])]);
602 negative.extend(frame(f32::INFINITY, &[]));
603 let e = read_streamed("t", &negative, 1).unwrap_err();
604 assert!(matches!(e, Error::Invalid(_)), "{e}");
605 let mut back = frame(1.0, &[]);
606 back.extend(frame(0.5, &[]));
607 let e = read_streamed("t", &back, 0).unwrap_err().to_string();
608 assert!(e.contains("byte 8"), "{e}");
609 }
610
611 fn dense(curves: i64, frames: i64, samples: Vec<f32>) -> Value {
612 let f = |k: &str, v: Value| (std::sync::Arc::<str>::from(k), v);
613 Value::Struct(vec![
614 f("m_CurveCount", Value::Int(curves)),
615 f("m_FrameCount", Value::Int(frames)),
616 f("m_SampleRate", Value::Float(2.0)),
617 f("m_BeginTime", Value::Float(0.0)),
618 f("m_SampleArray", Value::Array(Array::F32(samples))),
619 ])
620 }
621
622 #[test]
625 fn dense_counts_are_checked_against_the_samples() {
626 for (curves, frames) in [(i64::MAX, 3), (3, i64::MAX), (i64::MAX, i64::MAX), (1 << 40, 0)] {
627 let e = read_dense("t", &dense(curves, frames, vec![]))
628 .unwrap_err()
629 .to_string();
630 assert!(e.contains("dense clip"), "{e}");
631 }
632 let curves = read_dense("t", &dense(2, 2, vec![0.0, 10.0, 1.0, 20.0])).unwrap();
633 assert_eq!(curves.len(), 2);
634 assert_eq!(
635 curves[1].keys,
636 vec![key(0.0, 10.0, 0.0, 20.0), key(0.5, 20.0, 20.0, 0.0)]
637 );
638 assert_eq!(read_dense("t", &dense(0, 0, vec![])).unwrap(), vec![]);
639 }
640
641 #[test]
643 fn sampling_at_nan_does_not_panic() {
644 let curve = Curve {
645 storage: Storage::Constant,
646 keys: vec![key(0.0, 1.0, 0.0, 0.0), key(1.0, 2.0, 0.0, 0.0)],
647 };
648 assert!(curve.sample(f32::NAN).0.is_nan());
649 }
650
651 #[test]
655 fn path_hashes_are_the_crc32_of_the_path() {
656 assert_eq!(path_hash(""), 0);
657 assert_eq!(path_hash("scarf"), 2_702_068_705);
658 assert_eq!(path_hash("root/Lchain1/Lchain2/Lchain3"), 3_634_936_137);
659 assert_eq!(path_hash("root/Rchain1/Rchain2"), 356_844_446);
660 }
661}