using AssetRipper.Assets.Generics; using AssetRipper.Assets.Metadata; using AssetRipper.Assets.Utils; using AssetRipper.SourceGenerated.Classes.ClassID_1; using AssetRipper.SourceGenerated.Classes.ClassID_111; using AssetRipper.SourceGenerated.Classes.ClassID_4; using AssetRipper.SourceGenerated.Classes.ClassID_74; using AssetRipper.SourceGenerated.Classes.ClassID_90; using AssetRipper.SourceGenerated.Classes.ClassID_95; using AssetRipper.SourceGenerated.Extensions; using AssetRipper.SourceGenerated.Subclasses.GenericBinding; using AssetRipper.SourceGenerated.Subclasses.Utf8String; namespace AssetRipper.Processing.AnimationClips; /// /// A CRC32 lookup generator. /// /// /// TOS comes from . It presumably means "Table of Strings," but there doesn't seem to be any documentation on it. /// internal static class TosBuilder { private static IReadOnlyDictionary BuildTOS(this IAnimator animator) { if (animator.Has_HasTransformHierarchy_C95() && !animator.HasTransformHierarchy_C95) { return MakeNewDictionary(); } return animator.GameObject_C95.GetAsset(animator.Collection).BuildTOS(); } public static IReadOnlyDictionary FindTOS(this IAnimationClip clip, AnimationCache cache) { Dictionary tos = MakeNewDictionary(); foreach (IAvatar avatar in cache.CachedAvatars) { if (clip.AddAvatarTOS(avatar, tos)) { return tos; } } foreach (IAnimator animator in cache.CachedAnimators) { if (animator.ContainsAnimationClip(clip) && clip.AddAnimatorTOS(animator, tos)) { return tos; } } foreach (IAnimation animation in cache.CachedAnimations) { if (animation.ContainsAnimationClip(clip) && clip.AddAnimationTOS(animation, tos)) { return tos; } } return tos; } private static Dictionary MakeNewDictionary() { return new() { { 0, string.Empty } }; } private static bool AddAvatarTOS(this IAnimationClip clip, IAvatar avatar, Dictionary tos) { return clip.AddTOS(avatar.TOS_C90, tos); } private static bool AddAnimatorTOS(this IAnimationClip clip, IAnimator animator, Dictionary tos) { IAvatar? avatar = animator.Avatar_C95P; if (avatar != null && clip.AddAvatarTOS(avatar, tos)) { return true; } IReadOnlyDictionary animatorTOS = animator.BuildTOS(); return clip.AddTOS(animatorTOS, tos); } private static bool AddAnimationTOS(this IAnimationClip clip, IAnimation animation, Dictionary tos) { IGameObject go = animation.GameObject_C8.GetAsset(animation.Collection); IReadOnlyDictionary animationTOS = go.BuildTOS(); return clip.AddTOS(animationTOS, tos); } private static bool AddTOS(this IAnimationClip clip, IReadOnlyDictionary src, Dictionary dest) { if (!clip.Has_ClipBindingConstant_C74()) { return false; } AccessListBase bindings = clip.ClipBindingConstant_C74.GenericBindings; bool allFound = true; for (int i = 0; i < bindings.Count; i++) { uint bindingPath = bindings[i].Path; if (src.TryGetValue(bindingPath, out string? path)) { dest[bindingPath] = path; } else if (bindingPath != 0) { allFound = false; } } return allFound; } private static bool AddTOS(this IAnimationClip clip, AssetDictionary src, Dictionary dest) { if (!clip.Has_ClipBindingConstant_C74()) { return false; } AccessListBase bindings = clip.ClipBindingConstant_C74.GenericBindings; bool allFound = true; for (int i = 0; i < bindings.Count; i++) { uint bindingPath = bindings[i].Path; if (src.TryGetValue(bindingPath, out Utf8String? path)) { dest[bindingPath] = path.String; } else if (bindingPath != 0) { allFound = false; } } return allFound; } private static IReadOnlyDictionary BuildTOS(this IGameObject gameObject) { Dictionary tos = MakeNewDictionary(); gameObject.BuildTOS(gameObject, string.Empty, tos); return tos; } private static void BuildTOS(this IGameObject gameObject, IGameObject parent, string parentPath, Dictionary tos) { ITransform transform = parent.GetTransform(); foreach (ITransform? childTransform in transform.Children_C4P) { IGameObject child = childTransform?.GameObject_C4P ?? throw new NullReferenceException(); string path = string.IsNullOrEmpty(parentPath) ? child.NameString : $"{parentPath}/{child.NameString}"; uint pathHash = CrcUtils.CalculateDigestUTF8(path); tos[pathHash] = path; gameObject.BuildTOS(child, path, tos); } } }