Frame properties

Documentation of the storage of frame properties in the plain RTM format

This page is meant to supplement and clarify certain informations written on the community wiki about the plain RTM file format, prior knowledge of that article is required.

Introduction

While frame properties are documented on in the binarized RTM 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.

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.

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:

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];
};

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

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;
};

Disassembling

The following data disassembler code can be used to analyze plain RTM files in the ImHex editor:

#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;

Last updated