Skip to content
View in the app

A better way to browse. Learn more.

ResHax

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (ā‹®) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.
Help us keep the site running.

PES 2021 .gani/.mtar animation format: channel mapping, IK chains and UE5 reconstruction

Featured Replies

Hi everyone 😁,

I’m studying the PES 2021 .gani / .mtar animation format and trying to understand how the animation data is applied to the body skeleton.

My final goal is to reproduce a similar football animation pipeline in Unreal Engine 5 for study and experimentation.

So far, I have been looking at:

.ask skeleton files

.gani / .mtar animation data

QUAT12 / QUAT_DIFF rotation tracks

root motion

track/channel mapping

IK-like chains for legs and arms

I already have my own extracted local PES 2021 files, and I’m comparing them with public tools/templates such as Fox Engine templates and old Python/Noesis scripts.

The biggest doubts I’m trying to solve are these:

In PES 2021, is ChannelIndex supposed to map directly to a bone, or does it represent a track/unit that must be interpreted through chain rules such as IK legs/arms?

For .gani/.mtar QUAT_DIFF tracks, should the decoded quaternions be applied as local absolute rotations per frame, accumulated as deltas, or combined with a skeleton/render basis?

For bones such as feet/hands that appear to have multiple channels, are those channels simple additive/end-effector tracks, or part of a chain solver?

If anyone has old notes, parser scripts, format documentation, examples, or even just remembers how this system works, any help would be extremely valuable.

I am not asking for game assets or copyrighted content to redistribute. I’m only trying to understand the technical structure and the correct animation reconstruction logic.

Thanks a lot!!!!!Ā šŸ˜‰

  • Author
6 hours ago, Hazza12555 said:

are you able to send samples of anything you need help with, much appreciated!

Thanks, absolutely. I can prepare a small sample packet.

For context, we are working on PES2021 / Fox Engine animation reverse engineering, specifically football body animations from dt13.

We are not starting from zero. These are the findings we have already confirmed so far:

MTAR records appear to be 16 bytes:

Field0 = StrCode32(file_name)

Field1 = high 16 bits of StrCode48(file_name)

Field2 = clip offset

Field3 = clip size

The MOTION clip wrapper is parsed.

TrackHeader / TrackUnit / TrackData structure is parsed.

TrackDataBlob QUAT12 is structurally parsed:

FirstKey QUAT12

then repeated FrameDelta u8 + QUAT12

aligned to 2 bytes

VECTOR3 / VECTOR_DIFF 16-bit TrackDataBlob is structurally parsed.

ASK skeleton format is parsed:

header 0x08

bone record stride 0x2C

parent index at +0x08

local position float3 at +0x0C

local quaternion float4 at +0x1C

FRIG seems to map TrackUnits / channels to body animation bones.

We can already produce a Blender debug playback from a PES2021 kick animation, but the result is incomplete: lower limbs and terminal bones move, but arms/upper limbs are still not reconstructed correctly.

The current target sample is:

kick_short_0_0_inside_y0_000 anim_id: 2219 hit_frame: 8 hit_bone: sk_foot_r

The main things we need help understanding are:

How Fox/PES expects FRIG rig units to be composed, especially:

LEG_CHAIN

ARM_CHAIN

TWO_BONE / chain-like units

LOCAL_ORIENTATION / LOCAL_TRANSFORM if relevant

How to correctly apply chain channels versus simple bone channels.
For example, the foot/hand bones often have both a chain channel and a simple bone channel, while thigh/upperarm appear mainly through chain units. Our current suspicion is that a wrong chain-vs-simple selection may be causing arms to remain close to T-pose.

Whether body_anim_skel.ask or body_render_skel.ask should be used as the basis/bind pose for reconstructing the final pose.
body_anim_skel.ask has identity local quaternions, while body_render_skel.ask has non-identity basis quaternions.

How root channels should be composed:

ch0 sk_root_hip QUAT_DIFF ch1 sk_root_hip VECTOR_DIFF ch2 sk_root_hip QUAT ch3 sk_root_hip VECTOR3 ch4 dsk_hip QUAT static/one-key

Whether the static VECTOR3 values like 32766,32766,32766 on some limb chain roots are sentinel/default values, basis data, effector data, or something else.

I can share a compact packet with:

- one extracted MOTION clip sample - body_anim_skel.ask - body_render_skel.ask - body_skel.frig - CSV mapping TrackUnit/Channel -> bone name - decoded QUAT12 timeline JSON - current Blender debug viewer script - short markdown summary of confirmed structures

I would prefer to share a minimal research packet rather than large game archives. Let me know whether you prefer the sample packet here or via DM, and which exact files would be most useful to you.

  • Author

Hi, quick update from our PES 2021 .gani/.mtar reverse work.

We found a major issue in our previous testing: the clip we had been treating as a fuzzy candidate for anim_id 2229 was not 2229 at all.

Previous wrong candidate:
body_anime_file3.mtar / rec271 / off328448 / size11600

It resolves exactly to:
anim_id 7080
gkcatch_f02_diveline_3_0_y00

So the ā€œkarate / falling / divingā€ visual problem was actually because we were looking at a goalkeeper catch/dive animation.

The real exact 2229 is:

anim_id 2229
file_name kick_short_0_4_inside_y0_045_r_090
body_anime_file6.mtar / rec187 / off220608 / size5872
frame_count = 42

Using the FoxEngineTemplates anim_common.bt structure, we now get:

UnitCount = 15
SegmentCount = 27
FrameCount = 42
FrameRate = 10

The important part:

unit 0 seg0 = QUAT_DIFF, cbs 12
unit 0 seg1 = VEC_DIFF, cbs 16

All other relevant bone channels are mostly QUAT / VEC3 absolute. So our old decoder was mostly right for bones, but wrong for the root: it treated root delta channels as absolute.

We also tested EVP on the exact 2229 bin and found no EVP node inside this specific clip, so for this one we probably need to use hit_data / pivothit_info rather than internal foot events.

Question for anyone who has researched this, especially Aoba / shak-otay / Durik256:

Is the root delta accumulation expected to be:
q_acc = q_acc * q_delta
v_acc = v_acc + v_delta

Is unit 0 seg0/seg1 = QUAT_DIFF/VEC_DIFF consistent with your understanding of PES2019/PES2021 .gani?

Are foot/contact events usually stored outside the individual body .gani record, or only present in some clips?

For .ask, is Durik256’s structure still the best known reference, or is there a newer fmt_ask.py version available?

We can share small extracted test packets if useful.

  • Localization

To answer some of your questions.

Which skeleton is the basis: `body_anim_skel` or `body_render_skel`?

`body_anim_skel.ask` (identity basis) is the space the tracks are authored in.**

Both `.ask` files are version 1, 20 bones, header 0x08, bone stride 0x2C
(parent @+0x08, local pos float3 @+0x0C, local quat float4 @+0x1C). They describe the **same skeleton**:
every bone's offset *magnitude* is identical between the two files.

| convention | rotations | offset encodes |
|---|---|---|
| `body_anim_skel` | **all identity (0,0,0,1)** | full bone direction, Y-up "natural" frame |
| `body_render_skel` | non-identity basis quats | pure bone length down local **+X**; direction lives in the quat |

Evidence (offset magnitude is bit-identical; render collapses it onto +X):

```
boneĀ  Ā  Ā  Ā  Ā  Ā  |anim_off|Ā  Ā |render_off|Ā  Ā render_local_x
sk_leg_lĀ  Ā  Ā  Ā  Ā  0.4213Ā  Ā  Ā  Ā 0.4213Ā  Ā  Ā  Ā  Ā  +0.4213
sk_foot_lĀ  Ā  Ā  Ā  Ā 0.4418Ā  Ā  Ā  Ā 0.4418Ā  Ā  Ā  Ā  Ā  +0.4418
sk_forearm_lĀ  Ā  Ā  0.2900Ā  Ā  Ā  Ā 0.2900Ā  Ā  Ā  Ā  Ā  +0.2900
sk_upperarm_lĀ  Ā  Ā 0.0899Ā  Ā  Ā  Ā 0.0899Ā  Ā  Ā  Ā  Ā  +0.0899
...all 20 bones match...
```

Because every rest quat in `body_anim_skel` is identity, a bone's decoded track value **is** its local
rotation directly — no basis algebra needed. `body_render_skel`'s non-identity quats are only the
**skinning / bind basis** (the X-down-bone DCC convention). Pipeline:
apply tracks on `body_anim_skel` → compute world transforms → convert to the render bind for
skinning / UE5 retarget. Applying decoded local quats straight onto the render rest will corrupt the pose.

(Only difference beyond the basis: root_hip rest height 1.0961 vs 0.9655 — different global placement, expected.)

Ā 

Does ChannelIndex map directly to a bone, or to a track/unit interpreted through chain rules?

Ā 

NOT 1:1. It depends on the FRIG unit type.** `body_skel.frig` defines **15 units / 27 segments**
(header u32[3]=15, u32[4]=27 — exactly matching anim 2229). Unit types (word0 of each record):

| type | size | meaning | 1:1 with a bone? |
|---|---|---|---|
| 1 | 0x20 | root rotation | yes (root_hip) |
| 5 | 0x20 | root translation | yes (root_hip) |
| 4 | 0x20 | SIMPLE single-bone channel | **yes** |
| 3 | 0x40 | 2-bone chain (legs) | **no — fans out to 2–3 bones** |
| 8 | 0x40 | 3-bone chain (arms) | **no — fans out to 3 bones** |

Only type-4 (and root 1/5) segments map 1:1. Type-3/8 segments feed a **chain unit** that drives multiple bones.

Ā 

Feet/hands with multiple channels: additive end-effectors or chain inputs?

Ā 

both — they are the chain effector AND have an additive simple override.** Per the coverage
table below, foot_l/foot_r/hand_l/hand_r each appear as the **effector of a chain unit** *and* have their
own **type-4 simple channel** (units 4/6/12/14). The thigh/leg/shoulder/upperarm/forearm bones are pure
chain members with **no** simple override.

Ā 

Ā 

How are FRIG rig units composed (LEG_CHAIN / ARM_CHAIN / TWO_BONE / mirror)?

Ā 

From the 15 unit records:

- **LEG_CHAIN** = type-3, 2-bone: thigh → leg, effector = foot. Unit 3 holds **both** legs
Ā  (thigh_l, leg_l, thigh_r, leg_r, foot_l, foot_r) → this is the mirrored pair.
- **ARM_CHAIN** = type-8, 3-bone: shoulder → upperarm → forearm, effector = hand. Units 11 & 13.
- **Mirror** is encoded as a **sign float** inside the 0x40 record: unit 11 = āˆ’1.0, unit 13 = +1.0.
Ā  This matches the `MirrorL`/`MirrorR`, `LArm`/`RArm`/`LRArm`, `UBody`/`UBodyLArm`/`UBodyRArm` unit-name
Ā  strings found in the FRIG group section (offset 0x2d0+).
- The 0x40 records also carry packed 16-bit basis values (e.g. `16256` = 0x3F80 = high half of 1.0) —
Ā  chain basis / pole data.

Ā 

Why do the arms stay near T-pose?

Ā 

**Bone coverage (which units drive each bone):**

```
DIRECT type-4 channel (animate fine):
Ā  Ā  root_hip(rot+trans), dsk_hip, foot_l, foot_r, belly, chest, neck, head, hand_l, hand_r

CHAIN-ONLY, no direct channel:
Ā  Ā  thigh_l, leg_l, thigh_r, leg_rĀ  Ā  Ā  Ā  Ā  -> type-3 leg chain (units 3, 5)
Ā  Ā  shoulder_l/r, upperarm_l/r, forearm_l/r -> type-8 arm chain (units 11, 13)
```

The shoulder/upperarm/forearm bones have **no simple channel anywhere** — they only exist inside the
type-8 arm-chain units. A decoder that builds `ChannelIndex → bone` and applies segments as direct
per-bone rotations has **no entry** for these bones, so they never leave bind pose. Feet, hands, and the
whole trunk have type-4 channels, so they animate.

Ā 

Root channel composition / delta accumulation?

Ā 

- FRIG unit 0 = root_hip **rotation (type 1)**, unit 1 = root_hip **translation (type 5)** — consistent
Ā  with anim 2229's `unit0 seg0 = QUAT_DIFF (cbs12)`, `seg1 = VEC_DIFF (cbs16)`.
- Delta accumulation model `q_acc = q_acc * q_delta`, `v_acc = v_acc + v_delta` is consistent with this
Ā  split (root is a delta/diff track; most other bones are absolute QUAT/VEC3).Ā 

Ā 

If you would like to DM me which then we could both help eachother

Ā 

  • 2 weeks later...

Create an account or sign in to comment

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.