Skip to content

Motion Formats (mot files)

Brice Videau edited this page Jan 14, 2021 · 5 revisions

Motion files can be decoded using Bayonetta mot.bt, Bayonetta 2 mot.bt, Nier Automata mot.bt and Transformers Devastation mot.bt.

Bayonetta motion files encode skeletal animation data for a given model or model family, meaning models that share a common skeleton. Each animation is defined for a set number of frames.

Nine values per bone and per frame can be encoded (a tenth seems to be possible but is never found) each is associated to an index (6 is the unused index). These values are:

index value
0 translation along axis 0
1 translation along axis 1
2 translation along axis 2
3 rotation around axis 0
4 rotation around axis 1
5 rotation around axis 2
7 scaling along axis 0
8 scaling along axis 1
9 scaling along axis 2

When a triplet is not present its default value is used. Translations have parent relative offset as default value. Rotations have 0.0 as default values. Scaling values have a default value of 1.0.

It has to be noted that bone indexes found in motion files are absolute bone index and need to be converted for each model with a table given in the model file. Bone -1 is parent bone, which is usefull if the morion displaces a character for example.

Values can be encoded in a number of ways ranging from one float value per frame to key-frame interpolated data using half floats.

Motion files use a special type of half floats that will be referenced as pghalf (thanks Alquazar): they are IEEE floats with 1 bit sign, 6 bit exponent, 9 bit significand and a bias of 47. Using ruby they can be easily decoded thanks to the float-formats gem.

require 'float-formats'
Flt::IEEE.binary :IEEE_binary16_pg, significand: 9, exponent: 6, bias: 47
Flt::IEEE.binary :IEEE_binary16_pg_BE, significand: 9, exponent: 6, bias: 47, endianness: :big_endian
# Reading from binary data
f = Flt::IEEE_binary16_pg::from_bytes("\x00\x5e")
val = f.to(Float) # val == 1.0
g = Flt::IEEE_binary16_pg_BE::from_bytes("\x5e\x00")
val2 = g.to(Float) # val2 == 1.0
# Writing to binary data
h = Flt::IEEE_binary16_pg::new(1.0)
s = h.to_bytes # s == "\x00\x5e"

A c++ decoder can also be found here: Bayonetta noesis plugin. A faster c decoder/encoder can be found here libbin pghalf decoder.

Formats are slightly different between Bayonetta and Bayonetta 2 so each one will be presented individually.

Bayonetta

Header

Every motion file starts with a 16 byte header:

offset size c type description
0x00 4 char[4] "mot\x00"
0x04 2 uint16_t seems to be flags
0x06 2 int16_t frame count
0x08 4 uint32_t records offset
0x0C 4 uint32_t record count

Records

Records of 12 bytes follow at the offset specified in the header and with the given count. They are tightly packed.

offset size c type description
0x00 2 int16_t bone index
0x02 1 char value index
0x03 1 char record type
0x04 2 int16_t value count
0x06 2 int16_t always -1
0x08 4 float or uint32_t an union with a float value for type 0x00 or an offset for other types

Values

Each different record type corresponds to a different data encoding. Data are located at the offset given in the record (except for type 0x00). Only some of them are known:

Type 0x00

The value is constant over the animation and given by the record float value.

Type 0x01

The value for each frame is given in a float. Missing values should repeat the last one. Floats are packed.

offset size c type description
0x00 4 float value

Type 0x02

Hermit interpolated values using float data at key-frames.

offset size c type description
0x00 2 uint16_t absolute frame index
0x02 2 uint16_t padding
0x04 4 float value (p)
0x08 4 float incoming derivative value (m0)
0x0C 4 float outgoing derivative value (m1

Values between key-frames i and i+1, at frame index are computed using:

float p0 = keys[i].p
float m0 = keys[i].m1
float p1 = keys[i+1].p
float m1 = keys[i+1].m0
float t = (float)(index - keys[i].index)/(keys[i+1].index - keys[i].index)
float val = (2*t^3 - 3*t^2 + 1)*p0 + (t^3 - 2*t^2 + t)*m0 + (-2*t^3 + 3*t^2)*p1 + (t^3 - t^2)*m1;

Type 0x03

Quantized float values. One per frame. Missing values should repeat the last one.

Values are computed using the following formulas:
value: p + dp * cp

Header
offset size c type description
0x00 4 float value (p)
0x04 4 float value delta (dp)
Values
offset size c type description
0x00 2 uint16_t value quantum (cp)

Type 0x04

Hermit interpolated values using quantized float data at key-frames.

Values at a key-frame are computed using the following formulas:
value: p + dp * cp incoming derivative: m0 + dm0 * cm0
outgoing derivative: m1 + dm1 * cm1

Values between key-frames i and i+1, at frame index are computed using:

float p0 = value.p + value.dp * keys[i].cp
float m0 = value.m1 + value.dm1 * keys[i].cm1
float p1 = value.p + value.dp * keys[i+1].cp
float m1 = value.m0 + value.dm0 * keys[i+1].cm0
float t = (float)(index - keys[i].index)/(keys[i+1].index - keys[i].index)
float val = (2*t^3 - 3*t^2 + 1)*p0 + (t^3 - 2*t^2 + t)*m0 + (-2*t^3 + 3*t^2)*p1 + (t^3 - t^2)*m1;
Header

The record data starts with a 24 bytes header giving quantized values bounds.

offset size c type description
0x00 4 float value (p)
0x04 4 float value delta (dp)
0x08 4 float incoming derivative value (m0)
0x0C 4 float incoming derivative value delta (dm0)
0x10 4 float outgoing derivative value (m1)
0x14 4 float outgoing derivative value delta (dm1)
Values

Key-frames have a 8 byte size, they are densely packed.

offset size c type description
0x00 2 uint16_t absolute frame index
0x02 2 uint16_t value quantum (cp)
0x04 2 uint16_t incoming derivative quantum (cm0)
0x06 2 uint16_t outgoing derivative quantum (cm1)

Type 0x06

Same as 0x04 with reduced precision and relative frame index encoding

Header

12 byte size.

offset size c type description
0x00 2 pghalf value (p)
0x02 2 pghalf value delta (dp)
0x04 2 pghalf incoming derivative value (m0)
0x06 2 pghalf incoming derivative value delta (dm0)
0x08 2 pghalf outgoing derivative value (m1)
0x10 2 pghalf outgoing derivative value delta (dm1)
Values

4 byte size.

offset size c type description
0x00 1 uint8_t relative frame index
0x01 1 uint8_t value quantum (cp)
0x02 1 uint8_t incoming derivative quantum (cm0)
0x03 1 uint8_t outgoing derivative quantum (cm1)

Type 0x07

Same as 0x06 but with absolute frame index

Header

12 byte size.

offset size c type description
0x00 2 pghalf value (p)
0x02 2 pghalf value delta (dp)
0x04 2 pghalf incoming derivative value (m0)
0x06 2 pghalf incoming derivative value delta (dm0)
0x08 2 pghalf outgoing derivative value (m1)
0x10 2 pghalf outgoing derivative value delta (dm1)
Values

6 byte size.

offset size c type description
0x00 2 uint16_t absolute frame index
0x02 1 uint8_t dummy, probably for alignment
0x03 1 uint8_t value quantum (cp)
0x04 1 uint8_t incoming derivative quantum (cm0)
0x05 1 uint8_t outgoing derivative quantum (cm1)

Bayonetta 2

Bayonetta 2 format is very close to Bayonetta format. Offset in records are given relatively to the record start rather than relatively to the file start.

Header

Every motion file starts with a 16 byte header:

offset size c type description
0x00 4 char[4] "mot\x00"
0x04 4 uint32_t unknown
0x08 2 uint16_t seems to be flags
0x0A 2 int16_t frame count
0x0C 4 uint32_t records offset
0x10 4 uint32_t record count
0x14 4 uint32_t unknown
0x18 12 char[12] motion name

Records

Records of 12 bytes follow at the offset specified in the header and with the given count. They are tightly packed.

offset size c type description
0x00 2 int16_t bone index
0x02 1 char value index
0x03 1 char record type
0x04 2 int16_t value count
0x06 2 int16_t always -1
0x08 4 float or uint32_t an union with a float value for type 0x00 or an offset for other types

Values

Each different record type corresponds to a different data encoding. Data are located at the offset given in the record plus the offset of the start of the record (except for type 0x00). Most of them seem to b known:

Type 0x00

The value is constant over the animation and given by the record float value.

Type 0x01

The value for each frame is given in a float. Missing values should repeat the last one. Floats are packed.

offset size c type description
0x00 4 float value

Type 0x02

The value for each frame is quantized in a 16 bit integer. If some are missing the last is repeated.

Header

8 byte size.

offset size c type description
0x00 4 float value (p)
0x04 4 float value delta (dp)
Values

Densely packed uint16_t.

offset size c type description
0x00 2 uint16_t value quantum (cp)

Type 0x03

Same as 0x02 with reduced precision

Header

4 byte size

offset size c type description
0x00 2 pghalf value (p)
0x02 2 pghalf value delta (dp)
Values

Densely packed uint8_t.

offset size c type description
0x00 2 uint8_t value quantum (cp)

Type 0x04

Hermit interpolated values. Missing ranges before or after should repeat the first or last value.

Values at a key-frame are computed using the following formulas:
value: p
incoming derivative: m0
outgoing derivative: m1

Values between key-frames i and i+1, at frame index are computed using:

float p0 = values[i].p
float m0 = values[i].m1
float p1 = value[i+1].p
float m1 = value[i+1].m0
float t = (float)(index - values[i].index)/(vlaues[i+1].index - values[i].index)
float val = (2*t^3 - 3*t^2 + 1)*p0 + (t^3 - 2*t^2 + t)*m0 + (-2*t^3 + 3*t^2)*p1 + (t^3 - t^2)*m1;
Values

Densely packed

offset size c type description
0x00 2 uint16_t absolute frame index
0x02 2 uint16_t dummy, probably for alignment
0x04 4 float value (p)
0x08 4 float incoming derivative (m0)
0x0C 4 float outgoing derivative (m1)

Type 0x05

Hermit interpolated values using quantized float data at key-frames. Missing ranges before or after should repeat the first or last value.

Values at a key-frame are computed using the following formulas:
value: p + dp * cp
incoming derivative: m0 + dm0 * cm0
outgoing derivative: m1 + dm1 * cm1

Values between key-frames i and i+1, at frame index are computed using:

float p0 = value.p + value.dp * keys[i].cp
float m0 = value.m1 + value.dm1 * keys[i].cm1
float p1 = value.p + value.dp * keys[i+1].cp
float m1 = value.m0 + value.dm0 * keys[i+1].cm0
float t = (float)(index - keys[i].index)/(keys[i+1].index - keys[i].index)
float val = (2*t^3 - 3*t^2 + 1)*p0 + (t^3 - 2*t^2 + t)*m0 + (-2*t^3 + 3*t^2)*p1 + (t^3 - t^2)*m1;
Header

The record data starts with a 24 bytes header giving quantized values bounds.

offset size c type description
0x00 4 float value (p)
0x04 4 float value delta (dp)
0x08 4 float incoming derivative value (m0)
0x0C 4 float incoming derivative value delta (dm0)
0x10 4 float outgoing derivative value (m1)
0x14 4 float outgoing derivative value delta (dm1)
Values

Key-frames have a 8 byte size, they are densely packed.

offset size c type description
0x00 2 uint16_t absolute frame index
0x02 2 uint16_t value quantum (cp)
0x04 2 uint16_t incoming derivative quantum (cm0)
0x06 2 uint16_t outgoing derivative quantum (cm1)

Type 0x06

Same as 0x05 with reduced precision.

Header

12 byte size.

offset size c type description
0x00 2 pghalf value (p)
0x02 2 pghalf value delta (dp)
0x04 2 pghalf incoming derivative value (m0)
0x06 2 pghalf incoming derivative value delta (dm0)
0x08 2 pghalf outgoing derivative value (m1)
0x10 2 pghalf outgoing derivative value delta (dm1)
Values

4 byte size.

offset size c type description
0x00 1 uint8_t absolute frame index
0x01 1 uint8_t value quantum (cp)
0x02 1 uint8_t incoming derivative quantum (cm0)
0x03 1 uint8_t outgoing derivative quantum (cm1)

Type 0x07

Same as 0x06 with relative frame index.

Header

12 byte size.

offset size c type description
0x00 2 pghalf value (p)
0x02 2 pghalf value delta (dp)
0x04 2 pghalf incoming derivative value (m0)
0x06 2 pghalf incoming derivative value delta (dm0)
0x08 2 pghalf outgoing derivative value (m1)
0x10 2 pghalf outgoing derivative value delta (dm1)
Values

4 byte size.

offset size c type description
0x00 1 uint8_t relative frame index
0x01 1 uint8_t value quantum (cp)
0x02 1 uint8_t incoming derivative quantum (cm0)
0x03 1 uint8_t outgoing derivative quantum (cm1)

Type 0x08

Same as 0x07 but with absolute frame index (at least one relative frame index would have been > 255).

Header

12 byte size.

offset size c type description
0x00 2 pghalf value (p)
0x02 2 pghalf value delta (dp)
0x04 2 pghalf incoming derivative value (m0)
0x06 2 pghalf incoming derivative value delta (dm0)
0x08 2 pghalf outgoing derivative value (m1)
0x10 2 pghalf outgoing derivative value delta (dm1)
Values

5 byte size.

offset size c type description
0x00 2 uint16_t absolute frame index
0x02 1 uint8_t value quantum (cp)
0x03 1 uint8_t incoming derivative quantum (cm0)
0x04 1 uint8_t outgoing derivative quantum (cm1)

Nier Automata and TRANSFORMERS: Devastation

Nier Automata format is identical to Bayonetta 2 beside two changes:

  • one index is stored in a big endian way even on a small endian architecture

Header

Every motion file starts with a 16 byte header:

offset size c type description
0x00 4 char[4] "mot\x00"
0x04 4 uint32_t unknown
0x08 2 uint16_t seems to be flags
0x0A 2 int16_t frame count
0x0C 4 uint32_t records offset
0x10 4 uint32_t record count
0x14 4 uint32_t unknown
0x18 12 char[12] motion name

Records

Records of 12 bytes follow at the offset specified in the header and with the given count. They are tightly packed.

offset size c type description
0x00 2 int16_t bone index
0x02 1 char value index
0x03 1 char record type
0x04 2 int16_t value count
0x06 2 int16_t always -1
0x08 4 float or uint32_t an union with a float value for type 0x00 or an offset for other types

Values

Each different record type corresponds to a different data encoding. Data are located at the offset given in the record plus the offset of the start of the record (except for type 0x00). Most of them seem to b known:

Type 0x00

The value is constant over the animation and given by the record float value.

Type 0x01

The value for each frame is given in a float. Missing values should repeat the last one. Floats are packed.

offset size c type description
0x00 4 float value

Type 0x02

The value for each frame is quantized in a 16 bit integer. If some are missing the last is repeated.

Header

8 byte size.

offset size c type description
0x00 4 float value (p)
0x04 4 float value delta (dp)
Values

Densely packed uint16_t.

offset size c type description
0x00 2 uint16_t value quantum (cp)

Type 0x03

Same as 0x02 with reduced precision

Header

4 byte size

offset size c type description
0x00 2 pghalf value (p)
0x02 2 pghalf value delta (dp)
Values

Densely packed uint8_t.

offset size c type description
0x00 2 uint8_t value quantum (cp)

Type 0x04

Hermit interpolated values. Missing ranges before or after should repeat the first or last value.

Values at a key-frame are computed using the following formulas:
value: p
incoming derivative: m0
outgoing derivative: m1

Values between key-frames i and i+1, at frame index are computed using:

float p0 = values[i].p
float m0 = values[i].m1
float p1 = value[i+1].p
float m1 = value[i+1].m0
float t = (float)(index - values[i].index)/(vlaues[i+1].index - values[i].index)
float val = (2*t^3 - 3*t^2 + 1)*p0 + (t^3 - 2*t^2 + t)*m0 + (-2*t^3 + 3*t^2)*p1 + (t^3 - t^2)*m1;
Values

Densely packed

offset size c type description
0x00 2 uint16_t absolute frame index
0x02 2 uint16_t dummy, probably for alignment
0x04 4 float value (p)
0x08 4 float incoming derivative (m0)
0x0C 4 float outgoing derivative (m1)

Type 0x05

Hermit interpolated values using quantized float data at key-frames. Missing ranges before or after should repeat the first or last value.

Values at a key-frame are computed using the following formulas:
value: p + dp * cp
incoming derivative: m0 + dm0 * cm0
outgoing derivative: m1 + dm1 * cm1

Values between key-frames i and i+1, at frame index are computed using:

float p0 = value.p + value.dp * keys[i].cp
float m0 = value.m1 + value.dm1 * keys[i].cm1
float p1 = value.p + value.dp * keys[i+1].cp
float m1 = value.m0 + value.dm0 * keys[i+1].cm0
float t = (float)(index - keys[i].index)/(keys[i+1].index - keys[i].index)
float val = (2*t^3 - 3*t^2 + 1)*p0 + (t^3 - 2*t^2 + t)*m0 + (-2*t^3 + 3*t^2)*p1 + (t^3 - t^2)*m1;
Header

The record data starts with a 24 bytes header giving quantized values bounds.

offset size c type description
0x00 4 float value (p)
0x04 4 float value delta (dp)
0x08 4 float incoming derivative value (m0)
0x0C 4 float incoming derivative value delta (dm0)
0x10 4 float outgoing derivative value (m1)
0x14 4 float outgoing derivative value delta (dm1)
Values

Key-frames have a 8 byte size, they are densely packed.

offset size c type description
0x00 2 uint16_t absolute frame index
0x02 2 uint16_t value quantum (cp)
0x04 2 uint16_t incoming derivative quantum (cm0)
0x06 2 uint16_t outgoing derivative quantum (cm1)

Type 0x06

Same as 0x05 with reduced precision.

Header

12 byte size.

offset size c type description
0x00 2 pghalf value (p)
0x02 2 pghalf value delta (dp)
0x04 2 pghalf incoming derivative value (m0)
0x06 2 pghalf incoming derivative value delta (dm0)
0x08 2 pghalf outgoing derivative value (m1)
0x10 2 pghalf outgoing derivative value delta (dm1)
Values

4 byte size.

offset size c type description
0x00 1 uint8_t absolute frame index
0x01 1 uint8_t value quantum (cp)
0x02 1 uint8_t incoming derivative quantum (cm0)
0x03 1 uint8_t outgoing derivative quantum (cm1)

Type 0x07

Same as 0x06 with relative frame index.

Header

12 byte size.

offset size c type description
0x00 2 pghalf value (p)
0x02 2 pghalf value delta (dp)
0x04 2 pghalf incoming derivative value (m0)
0x06 2 pghalf incoming derivative value delta (dm0)
0x08 2 pghalf outgoing derivative value (m1)
0x10 2 pghalf outgoing derivative value delta (dm1)
Values

4 byte size.

offset size c type description
0x00 1 uint8_t relative frame index
0x01 1 uint8_t value quantum (cp)
0x02 1 uint8_t incoming derivative quantum (cm0)
0x03 1 uint8_t outgoing derivative quantum (cm1)

Type 0x08

Same as 0x07 but with absolute frame index (at least one relative frame index would have been > 255).

Header

12 byte size.

offset size c type description
0x00 2 pghalf value (p)
0x02 2 pghalf value delta (dp)
0x04 2 pghalf incoming derivative value (m0)
0x06 2 pghalf incoming derivative value delta (dm0)
0x08 2 pghalf outgoing derivative value (m1)
0x10 2 pghalf outgoing derivative value delta (dm1)
Values

5 byte size.

offset size c type description
0x00 2 uint16_t absolute frame index (big endian order)
0x02 1 uint8_t value quantum (cp)
0x03 1 uint8_t incoming derivative quantum (cm0)
0x04 1 uint8_t outgoing derivative quantum (cm1)