---
description: Documentation of the storage of frame properties in the plain RTM format
---

# Frame properties

{% hint style="info" %}
This page is meant to supplement and clarify certain informations written on the [community wiki](https://community.bistudio.com/wiki/Rtm_\(Animation\)_File_Format) about the plain RTM file format, prior knowledge of that article is required.
{% endhint %}

## Introduction

While frame properties are documented on in the [binarized RTM](https://community.bistudio.com/wiki/Rtm_Binarised_File_Format) format, there is no reference for their possible storage in the plain RTM format.

## Data blocks

The format specifications on the community wiki decribe the plain RTM format as a singular data block, with a header signature (`RTM_0101`) similar to other Arma 3 file formats. However, after inspecting the animation dialogs of the **Object Builder** application, it is clear that the format should be able to store frame properties in some form.

<figure><img src="https://3093199008-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW202g4rqtsk2O6cTvlfr%2Fuploads%2FjxIHgt1ePm35GkUj66vo%2Fimage.png?alt=media&#x26;token=742c5382-6145-4e98-b33e-0afdedfb285a" alt=""><figcaption><p>Keyframes and a single frame property</p></figcaption></figure>

After setting up a basic animation, and adding a (supposedly) frame property to it at the 0.5 time mark with the `StepSound` name (this is the only frame property name currently found in Arma 3 animations), the data could be exported to a plain RTM file.

<figure><img src="https://3093199008-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FW202g4rqtsk2O6cTvlfr%2Fuploads%2FfLLHFj1KlRQ5ngThmWXX%2Fimage.png?alt=media&#x26;token=105a122d-ac56-4a87-849e-af41ba8838c6" alt=""><figcaption></figcaption></figure>

Inspecting the saved file reveales that the frame properties are stored in a separate data block, stored before the transformation data. This block has a signature of `RTM_MDAT`. After some experimenting, the data structure of the block is the following:

{% code fullWidth="true" %}
```c
struct lascii {
    byte length; // max length is 255, Object Builder seems to overflow the number
    char value[length];
};
struct FrameProp {
    float phase;
    lascii name;
    lascii value;
};
struct RTM_MDAT {
    char signature[8]; // "RTM_MDAT"
    ulong padding; // 0
    ulong nProps;
    FrameProp props[nProps];
};
```
{% endcode %}

With this, the complete structure of the plain RTM format is the following:

{% code fullWidth="true" %}
```c
struct lascii {
    byte length; // max length is 255, Object Builder seems to overflow the number
    char value[length];
};
struct FrameProp {
    float phase;
    lascii name;
    lascii value;
};
struct RTM_MDAT {
    char signature[8]; // "RTM_MDAT"
    ulong padding; // 0
    ulong nProps;
    FrameProp props[nProps];
};

struct BoneName {
    char name[32]; // asciiz name + padding up to 32 bytes (might contain junk in the padding)
};
struct Transform {
    BoneName bone;
    float matrix[12]; // truncated 4x4 DirectX transform matrix -> 4th column is not stored
};
struct Frame {
    float phase;
    Transform transforms[nBones];
};
struct RTM_0101 {
    char signature[8]; // "RTM_0101"
    float motion[3];
    ulong nFrames;
    ulong nBones;
    BoneName bones[nBones];
    Frame frames[nFrames];
};

struct RTM_File {
    RTM_MDAT frameProps; // optional, preceeds RTM_0101 block
    RTM_0101 anim;
};
```
{% endcode %}

## Disassembling

The following data disassembler code can be used to analyze plain RTM files in the [ImHex](https://imhex.werwolv.net/) editor:

{% code fullWidth="true" %}
```c
#pragma endian little
#pragma pattern_limit 99999999

#include <std/mem.pat>

using byte = u8;
using ulong = u32;

struct lascii {
    byte len;
    char value[len];
} [[format("lasciiName")]];

fn lasciiName(ref lascii string) {
    return string.value;
};

struct BoneName {
    char name[32];
} [[static]];

struct BoneTransform {
    char bone[32];
    float matrix[12];
} [[static]];

struct RtmFrame {
    float phase;
    BoneTransform transforms[parent.nBones];
} [[static]];

struct RTM_FrameProp {
    float phase;
    lascii name;
    lascii value;
};

struct RTM_Block {
    char sign[8];

    if (sign == "RTM_MDAT") {
        padding[4];
        ulong count_props;
        RTM_FrameProp props[count_props];
    } else if (sign == "RTM_0101") {
        float     motion[3];
        ulong     nFrames;
        ulong     nBones;
        BoneName    BoneNames[nBones];
        RtmFrame    Frames[nFrames];
    }
} [[format("blockName")]];

fn blockName(ref RTM_Block block) {
    return block.sign;
};

// Pattern parsing start
RTM_Block blocks[while(!std::mem::eof())] @ 0x00;
```
{% endcode %}
