rbx_binary 0.5.0

Implementation of Roblox's binary model (rbxm) and place (rbxl) file formats
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
# Roblox Binary Model Format, Version 0
This document is based on:
- [*ROBLOX File Format* by Gregory Comer]http://www.classy-studios.com/Downloads/RobloxFileSpec.pdf
- [LibRbxl by Gregory Comer]https://github.com/GregoryComer/LibRbxl
- [rbxfile by Anaminus]https://github.com/RobloxAPI/rbxfile
- [Roblox-File-Format by CloneTrooper1019]https://github.com/CloneTrooper1019/Roblox-File-Format
- Observing `rbxm` and `rbxl` output from Roblox Studio

## Contents
- [File Structure]#file-structure
- [File Header]#file-header
- [Chunks]#chunks
	- [`META` Chunk]#meta-chunk
	- [`INST` Chunk]#inst-chunk
	- [`PROP` Chunk]#prop-chunk
	- [`PRNT` Chunk]#prnt-chunk
	- [`END` Chunk]#end-chunk
- [Data Types]#data-types
	- [String]#string
	- [Bool]#bool
	- [Int32]#int32
	- [Float32]#float32
	- [Float64]#float64
	- [UDim]#udim
	- [UDim2]#udim2
	- [Ray]#ray
	- [Faces]#faces
	- [Axis]#axis
	- [BrickColor]#brickcolor
	- [Color3]#color3
	- [Vector2]#vector2
	- [Vector3]#vector3
	- [CFrame]#cframe
	- [Referent]#referent
	- [Vector3int16]#vector3int16
	- [NumberSequence]#numbersequence
	- [ColorSequence]#colorsequence
	- [NumberRange]#numberrange
	- [Rect2D]#rect2d
	- [PhysicalProperties]#physicalproperties
	- [Color3uint8]#color3uint8
	- [Int64]#int64
- [Data Storage Notes]#data-storage-notes
	- [Interleaved Array]#interleaved-array

## File Structure
1. File Header
2. Chunks
	1. Zero or one `META` chunks
	2. Zero or more `INST` chunk
	3. Zero or more `PROP` chunks
	4. One `PRNT` chunk
	5. One `END` chunk

## File Header
Every file starts with:

<table>
	<tr>
		<th width="40">0</th>
		<th width="40">1</th>
		<th width="40">2</th>
		<th width="40">3</th>
		<th width="40">4</th>
		<th width="40">5</th>
		<th width="40">6</th>
		<th width="40">7</th>
		<th width="40">8</th>
		<th width="40">9</th>
		<th width="40">10</th>
		<th width="40">11</th>
		<th width="40">12</th>
		<th width="40">13</th>
		<th width="40">14</th>
		<th width="40">15</th>
	</tr>
	<tr>
		<td colspan="8">Magic number (<code>&lt;roblox!</code>)</td>
		<td colspan="6">Signature (<code>89 ff 0d 0a 1a 0a</code>)</td>
		<td colspan="2">Version (<code>0</code>)</td>
	</tr>
	<tr>
		<td colspan="4">Number of instance types (<code>u32</code>)</td>
		<td colspan="4">Number of instances (<code>u32</code>)</td>
		<td colspan="8">Reserved bytes (always 0)</td>
	</tr>
</table>

## Chunks
Every chunk starts with:

<table>
	<tr>
		<th width="40">0</th>
		<th width="40">1</th>
		<th width="40">2</th>
		<th width="40">3</th>
		<th width="40">4</th>
		<th width="40">5</th>
		<th width="40">6</th>
		<th width="40">7</th>
		<th width="40">8</th>
		<th width="40">9</th>
		<th width="40">10</th>
		<th width="40">11</th>
		<th width="40">12</th>
		<th width="40">13</th>
		<th width="40">14</th>
		<th width="40">15</th>
	</tr>
	<tr>
		<td colspan="4">Chunk name</td>
		<td colspan="4">Compressed length (<code>u32</code>)</td>
		<td colspan="4">Uncompressed length (<code>u32</code>)</td>
		<td colspan="4">Reserved bytes (always 0)</td>
	</tr>
	<tr>
		<td colspan="16">Chunk data</td>
	</tr>
</table>

If **chunk name** is less than four bytes, the remainder is filled with zeros.

If **compressed length** is zero, **chunk data** contains **uncompressed length** bytes of data for the chunk.

If **compressed length** is nonzero, **chunk data** contains an LZ4 compressed block. It is **compressed length** bytes long and will expand to **uncompressed length** bytes when decompressed.

When the **chunk data** is compressed, it is done so using the [LZ4](https://github.com/lz4/lz4) compression algorithm.

When documentation for individual chunks uses the term "chunk data", it refers to **chunk data** after it has been decompressed, if necessary.

### `META` Chunk
| `META` Chunk Data |
| ----------------- |
| Number of entries (`u32`) |
| Metadata Entries (fills rest of chunk) |

| Metadata Entry |
| ----- |
| Key ([String]#string) |
| Value ([String]#string) |

The Metadata chunk (`META`) is a map of strings to strings. It represents metadata about the model, such as whether it was authored with `ExplicitAutoJoints` enabled.

There should be zero or one `META` chunks.

Observed metadata entries and their values:

- `ExplicitAutoJoints`: `true` or `false`

### `INST` Chunk
| `INST` Chunk Data |
| ----------------- |
| Type ID (`u32`) |
| Type Name ([String]#string) |
| Object Format (`u8`) |
| Number Instances (`u32`) |
| Instance Referents ([Referent]#referent Array) |
| Service Markers (`u8` * number instances) |

The Instance chunk (`INST`) defines a type of instance, how many of them there are in this file, and what referent IDs they have.

There should be one `INST` chunk for each type of instance defined.

There are two forms of the `INST` chunk determined by the **object format** field:

- `0`: regular
- `1`: service

If the object format is **regular**, the service markers section will not be present.

If the object format is **service**, the service markers section contains `1` repeated for the number of instances of that type in the file. If this field is not set, Roblox may create duplicate copies of services, like in [rojo-rbx/rbx-dom#11](https://github.com/rojo-rbx/rbx-dom/issues/11).

**Type ID** must be unique and ideally sorted monotonically among all `INST` chunks. It's used later in the file to refer to this type.

**Type Name** should match the `ClassName` specified on an instance in Roblox.

The length of the **Instance Referents** array must match the **Number of Instances** field.

### `PROP` Chunk
| `PROP` Chunk Data |
| ----------------- |
| Type ID (`u32`) |
| Property Name ([String]#string) |
| Data Type (`u8`) |
| Values (array of data) |

The property chunk (`PROP`) defines a single property for a single instance type.

There should be one `PROP` chunk per property per instance type.

Because of the shape of this chunk, every instance of a given type must have the same properties specified with the same times. Put another way, if any instance in the file defines a property, all other instances of the same type must also specify that property!

**Type ID** defines the instance that this property applies to as defined in a preceding `INST` chunk.

**Property Name** defines the serializable name of the property. Note that this is not necessarily the same as the name reflected to Lua, which is sometimes referred to as the _canonical name_.

**Data Type** is

### `PRNT` Chunk
| `PRNT` Chunk Data |
| ----------------- |
| Version (`u8`, zero) |
| Number of Objects (`u32`) |
| Object Array ([Referent]#referent Array) |
| Parent Array ([Referent]#referent Array) |

The parent chunk (`PRNT`) defines the hierarchy relationship between every instance in the file.

There should be exactly one `PRNT` chunk.

**Version** field should currently always be zero.

**Number of Objects** should be the same as the number of instances in the file header chunk, since each object should have a parent.

**Object Array** and **Parent Array** should both have length equal to **Number of Objects**. The parent of the ID at position *N* in the **Object Array** is a child of the ID at position *N* in the **Parent Array**.

A null parent referent (`-1`) indicates that the object is a root instance. In a place, that means the object is a child of `DataModel`. In a model, that means the object should be placed directly under the object the model is being inserted into.

### `END` Chunk
| `END` Chunk Data |
| ---------------- |
| Magic Value `</roblox>` |

The ending chunk (`END`) signifies the end of the file.

The `END` chunk should not be compressed, since it's used as a rough form of file validation when uploading places to the website.

## Data Types

### String
**Type ID `0x01`**

| String |
| ------ |
| String length in bytes (u32) |
| Data |

String data is UTF-8 encoded.

### Bool
**Type ID `0x02`**

### Int32
**Type ID `0x03`**

**Untransformed integers**, generally in header data, are little-endian and two's complement. Integers are untransformed unless denoted otherwise.

**Transformed integers**, normally used in property data, are big-endian and are transformed and untransformed via:

```rust
fn transform_i32(value: i32) -> i32 {
	if value >= 0 {
		value * 2
	} else {
		2 * -value - 1
	}
}

fn untransform_i32(value: i32) -> i32 {
	if value % 2 == 0 {
		value / 2
	} else {
		-(value +1 1) / 2
	}
}
```

Integers can also be transformed via bitwise ops to avoid branches:

```rust
fn transform_i32(value: i32) -> i32 {
	(value << 1) ^ (value >> 31)
}

fn untransform_i32(value: i32) -> i32 {
	((value as u32) >> 1) as i32 ^ -(value & 1)
}
```

### Float32
**Type ID 0x04**

### Float64
**Type ID 0x05**

### UDim
**Type ID 0x06**

### UDim2
**Type ID 0x07**

### Ray
**Type ID 0x08**

### Faces
**Type ID 0x09**

### Axis
**Type ID 0x0A**

### BrickColor
**Type ID 0x0B**

### Color3
**Type ID 0x0C**

### Vector2
**Type ID 0x0D**

### Vector3
**Type ID 0x0E**

### CFrame
**Type ID 0x10**

### Enum
**Type ID 0x12**

### Referent
**Type ID 0x13**

Referents are stored as transformed 32-bit signed integers. A value of `-1` (untransformed) indicates a null referent.

When reading an [Interleaved Array](#interleaved-array) of referents, they should be read accumulatively. In other words, the value of each referent id should be itself, plus its previous value.

❌ **Without Accumulation**

<table>
	<tr>
		<th colspan="6">Referents</th>
	</tr>
	<tr>
		<th width="40">1619</th>
		<th width="40">1</th>
		<th width="40">4</th>
		<th width="40">2</th>
		<th width="40">3</th>
		<th width="40">5</th>
	</tr>
</table>

✔ **With Accumulation**

<table>
	<tr>
		<th colspan="6">Referents</th>
	</tr>
	<tr>
		<th width="40">1619</th>
		<th width="40">1620</th>
		<th width="40">1624</th>
		<th width="40">1626</th>
		<th width="40">1629</th>
		<th width="40">1634</th>
	</tr>
</table>

### Vector3int16
**Type ID 0x14**

### NumberSequence
**Type ID 0x15**

### ColorSequence
**Type ID 0x16**

### NumberRange
**Type ID 0x17**

### Rect2D
**Type ID 0x18**

### PhysicalProperties
**Type ID 0x19**

### Color3uint8
**Type ID 0x1A**

### Int64
**Type ID 0x1B**

## Data Storage Notes

### Interleaved Array
Arrays of many types in property data have their bytes interleaved.

For example, an array of 4 bit integers normally represented as:

<table>
	<tr>
		<td><b>A0</b></td>
		<td><b>A1</b></td>
		<td><b>A2</b></td>
		<td><b>A3</b></td>
		<td>B0</td>
		<td>B1</td>
		<td>B2</td>
		<td>B3</td>
		<td>C0</td>
		<td>C1</td>
		<td>C2</td>
		<td>C3</td>
	</tr>
</table>

Would become, after interleaving:

<table>
	<tr>
		<td><b>A0</b></td>
		<td>B0</td>
		<td>C0</td>
		<td><b>A1</b></td>
		<td>B1</td>
		<td>C1</td>
		<td><b>A2</b></td>
		<td>B2</td>
		<td>C2</td>
		<td><b>A3</b></td>
		<td>B3</td>
		<td>C3</td>
	</tr>
</table>

Note that arrays of integers are generally subject to both interleaving and integer transformation.