Compressed replay file format for Echo VR based on .milk
C#MIT
.butter Replay Format
The .butter format is originally based on the work on .milk by Exhibit.
The goals of this format are as follows:
Smallest possible file size
Minimal data loss
Some precision is lost through lower bit length numbers
Precision in timing of events should not be compromised
Data complete
All parts of the API response should be included
Currently team names are intentionally not included
Keyframes
In general, data for each frame in this format is only stored as a diff of the previous frame, however, a full reset happens every N frames. This allows decompression of partial replays and streaming, as well as prevents precision errors from building up over time. Keyframe intervals can be customized depending on the recording rate and use case, but keyframes cannot be spaced more than 1 minute of real time apart due to the timestamp encoding. If there is more than 1 minute between keyframes, a keyframe should automatically be inserted.
The start of a keyframe is denoted by the 0xFEFC header instead of the normal 0xFEFE header.
Frames
Each frame represents the data retrieved from a single call to the game's API. Any numerical value in a frame is only stored as the difference from the previous frame, except for keyframes.
File structure
Bytes
Data
(27-45) + (18-36) * n
File Header (n=total number of players in the file
Below are estimated sizes for each frame. To get approximate full uncompressed file size, multiply the number by the number of frames. Average frame sizes are around 380 bytes experimentally.
Min
Max (8 players)
Keyframe
28
1112
Normal Frame
23
1106
Map Byte
Below is the bit flags structure for the Map Byte
Bit number
Field name
0
Public or Private, Public = 0, Private = 1
1-3
Map name, see coding table below
4-7
Currently Unused Bits
Map name coding
Index
Map Name
0
uncoded
1
mpl_lobby_b2
2
mpl_arena_a
3
mpl_combat_fission
4
mpl_combat_combustion
5
mpl_combat_dyson
6
mpl_combat_gauss
7
mpl_tutorial_arena
Inclusion bitmask
2a/4c represents 2 bytes in Arena, 4 bytes in Combat
Orientations are usually presented in the API as 3 sets of XYZ direction vectors, but this can be encoded in 4 bytes with minimal precision loss by using smallest three compression.
The orientation is converted to a Quaternion, then the component with the smallest absolute value is replaced by its index (in 2 bits). The other three values are recorded using 10 bits each.