Members Eilde Posted 12 hours ago Members Posted 12 hours ago I was asked to take a look at the animations used for Crash Bandicoot Wrath of Cortex that is on three different consoles. Xbox, Gamecube, and PS2. They wanted help and I took a quick look at it. After doing such I thought I should make a page here and put up my findings to help others with this. Some of this is a copy /paste of my reply to the one that asked me to have a look at it to save time. .HGO is the model/skeletal format that I should mention before hand. .ANI and it's companion file .BSA are the animations. The "Nuanim" animation format is the ANI and BSA together. " The .ani data behaves like a memory-structure dump from the game engine rather than a clean, self-describing interchange format. The strongest tell is the “pointer table” pattern. There is a run of 47 u32 values that look like absolute addresses (all sharing the same high 16 bits). Subtracting that common high-16 “base” yields valid in-file offsets. That pattern is typical of a format that was originally pointers in RAM, written out with minimal fixups. .ani is likely an engine-native serialized structure. Within each track block, I repeatedly seen something that parses cleanly as type_flags (u32), key_ptr (u32 “absolute pointer” into this same block), key_count (u32), pad (u32). Then key_count keys that look exactly like time (float32), value (float32), tanIn (float32), tanOut (float32). Times are mostly monotonic and look like frame indices (0..N). So the curve container is real. The .hgo contains "On xbox" an NTBL string table that yields plausible bone names (at least enough to map “track i → bone name i” in a consistent way). So they probably have track ordering and a skeleton name list. "The following here is what I tried and failed" A base explanation. I generated BVH by assuming “3 curves per bone” = Euler X/Y/Z rotations (with all six Euler orders). Same but with different Hermite tangent interpretations (dt-scaled vs not, clamping, wrapping). “3 curves per bone” = quaternion vector components (x,y,z) with implied w = sqrt(1 − x² − y² − z²) and continuity sign fixes. Doing this is why I mainly only got "spaghetti". A mostly coherent-looking hierarchy (so offsets / parentage aren’t totally random), one “sane” motion that looks like a repeated arc/hammer (likely a translation-like signal), the rest flailing at insane speeds (classic “wrong semantic decode,” not just wrong axis). The curves are real, but my interpretation of what each curve represents is wrong. " That is where I basically left off but am going to add additional information and again copy/pastes of what I already wrote to save time. " Now, here are some "guesses". One or more of the following are true. I would need more time to figure it out. One. The curves are not Euler components.. Two. The curves are not quaternion components in the simple sense.. Three. The .ani does not contain enough metadata to know what each curve means — the meaning is supplied by the companion .bsa and/or other per-skeleton structures. Four. type_flags (and/or .bsa) is encoding critical information such as channel type (rotation vs translation vs scale vs something else), packing mode (raw float vs quantized), component mapping (which curve is which component), unit (radians/turns/normalized), interpolation mode (Hermite/Bezier/slerp/etc.), or sign reconstruction (very common with quaternion compression). To summarize all this... I can parse the curve blobs, but I do not yet know how to apply them correctly to a skeleton. My assumptions about the BSA are the big bullets here. I have not decoded .bsa. That is likely fatal. I thought I did..... But as they say.. can't spell assumption without spelling ass first..... I should not have made so many assumptions. " For those that want to figure out this animation format and continue on, there is a github dedicated to reverse engineering the game Crash Bandicoot Wrath of Cortex using the gamecube version as a base due to it's debug symbols. Also, you would need to do the following as well and again is going to be a copy/paste so I don't have to write everything out again. " First, you need to properly decode the .BSA files. Second, you will need to identify the "type_flags". Third, Confirm the rotation representation used by NUANIM in this game. For each joint, produce a stable quaternion per frame that does not explode. My method was probably not the best using BVH because it is is a bad target for tricky rigs because it has limited support for per-bone pre-rotations and coordinate conventions. Even with a correct decode, BVH can still look “off.” I went this route for speed... Not sanity. You will be better off with glTF animation on the same joint names as crash.gltf, or a Blender importer that writes quaternions directly to pose bones. BVH was just faster for me for testing. I was trying to see if I could get ANYTHING correct. The solution to how these animations work are in the XBE and ELF files for the xbox and ps2 versions. " And that is about it. I am not going to continue looking into these animations unless someone pays me. Hopefully this helps others. This was my 1 day poke at the animations. I know some fan guru of the Crash Bandicoot game will figure it out with time. Hopefully this helps you out. Godspeed.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now