Skip to content

Grub bundles

ForzaTechData modelApplies toFH6

A Grub bundle is the general container of ForzaTech: a header, a table of typed blobs, and for each blob a list of small metadata records. The same container holds .modelbin models, .materialbin materials, .pb and .swatchbin textures, .shaderbin shader data, .crowdbin crowds and the car colour lists (ManufacturerColors.bin); what a bundle is for is decided by its blob tags. Proven Over the install, every entry whose first four bytes are burG has one of those seven extensions, and every .modelbin, .pb, .swatchbin, .materialbin and .shaderbin is a bundle. Proven

A bundle can sit inside a blob of another: a model’s MatI blobs are material-instance bundles, and a crowd’s ClsL, Clas, Mdls, Modl, MtlI and Skls blobs are bundles, some of them holding bundles in turn. Proven

Every statement on this page marked Proven · FH6 holds on every bundle of the Steam build’s install, the nested ones included: the 526,129 bundles of the Tokyo track’s GeoChunks and the zips under media (196,620 models, 191,602 .pb and 126,527 .swatchbin textures, 9,707 materials, 987 shaders, 682 car colour lists and 4 crowds) and the 1,526,529 bundles nested in their blobs. A reader that refuses any value or byte the layout below does not account for reads all of them, and Doliman100’s reader gives the same blobs, metadata records and payloads on every one.

A tag is a u32 whose big-endian bytes spell its name: Modl is stored as the bytes ldoM, and the bundle’s own tag Grub as the bytes burG. Proven Names below are written the way the tag reads, not the way the bytes run.

Grub bundle header20 bytesFH690% known

Little-endian. Version 1.1 in every bundle.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32 = Grub (bytes burG)tagProven
0x41u8 = 1version majorProven
0x51u8 = 1version minorProven
0x62u16 = 0reservedUnknown0 in every bundle; readers of older versions take a u16 blob count here
0x84u32header sizeProvenwhere the first blob's data starts
0xC4u32total sizeProventhe bundle's length
0x104u32blob countProven

The blob records follow at 20, 24 bytes each. Proven

Blob record24 bytesFH683% known
ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32tagProven
0x41u8version majorProven
0x51u8version minorProven
0x62u16metadata countProven8-byte metadata records
0x84u32metadata offsetProvenfrom the bundle's start
0xC4u32data offsetProvenfrom the bundle's start
0x104u32data sizeProven
0x144u32data size againUnknownequal to the data size in every blob
Blob metadata record8 bytesFH6100% known

A blob's records sit one after another at its metadata offset.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32tagProvenName, Id, BBox, TXCH, ATST, XTRA, ...
0x42u16size and versionProvenpayload size = value >> 4 (0xFFF: see below), version = value & 0xF
0x62u16payload offsetProvenfrom this record's own start

A payload of 0xFFF bytes or more has 0xFFF in its size field and its true size in a u32 just before it; the record’s offset points past that u32. Proven Only 16 VDCL records of shader bundles are that large. Proven A reader that takes the 12-bit field as the size reads the first 0xFFF bytes of such a payload and, if it packs payloads itself, misplaces the records after it.

The parts follow one another with no byte unaccounted for. Proven

  1. The header and the blob records.
  2. For each blob in record order: its metadata records, then their payloads in record order, packed with no gaps. A blob without metadata has its metadata offset where the next blob’s records would start.
  3. The header ends where the metadata ends, rounded up to 4 with zero bytes, except in 11 .swatchbin textures whose header ends unaligned where the metadata does, so that their first blob’s data starts unaligned. Proven The install’s 11 TXCB blobs of version 2.0 are rain masks in the weather textures; that they are those same 11 is Observed on the bundles inspected.
  4. Each blob’s data in record order: the first at the header size, each next one at the previous one’s end rounded up to 4, the gap zero.
  5. The bundle ends where the last blob’s data ends, rounded up to 4 with zeros, except in 8,418 .swatchbin textures, which end where the data does, 1 to 3 bytes short of that. Proven

A metadata Name payload is a string without a terminator and can be empty. Proven

Tag Used in Holds Confidence
Modl model counts, LOD range, material variant table (Models) Observed
Mesh model per-mesh draw record Observed
VLay, ILay model vertex layout; ILay only in crowd models Observed
IndB, VerB model index and vertex buffers Observed
MBuf model morph target deltas Observed
Skel, Skin model skeleton and skin weights; every model has a Skel Observed
Mrph cars names of morph weight slots Observed
MatI model a material instance: a nested bundle holding a MATI blob (the material it derives from) and an MTPR blob Observed
MATL, MTPR, MTUD material (.materialbin) the material, its shader parameters (Materials) Observed
PARM, VARS, CBMP, TXMP, SPMP, CBEX, DFPR, LSCE, DBLS, TRGT, TXRF, VERS, VERT, SHUD shader parameter names, variants, register maps and programs (Shader containers) Observed
TXCB with TXCH metadata texture mip chain and its header (Swatch textures) Observed
MNCL car colour list factory paints (Cars) Observed
ClsL, Clas, Mdls, Skls, Anim, MdMt, ExtI, MtlI, RigI, MtOv crowd crowd classes, models, skeletons and animations, as nested bundles Observed

The blob versions over the install, nested bundles included: Anim 1.0, CBEX 1.0, CBMP 3.1, Clas 3.0, ClsL 1.0, DBLS 1.10, DFPR 2.1, ExtI 1.0, ILay 1.1, IndB 1.0, LSCE 1.10, MATI 1.0, MATL 1.2, MBuf 1.1, MNCL 2.0, MTPR 2.0, MTUD 1.0, MatI 1.0, MdMt 1.0, Mdls 1.0, Mesh 1.12 and 1.13, Modl 1.0 (crowds), 1.4 and 1.5, Mrph 0.0, MtOv 0.0, MtlI 1.0, PARM 1.2, RigI 1.0, SHUD 1.0, SPMP 3.1, Skel 1.0, Skin 1.1, Skls 1.0, TRGT 1.1, TXCB 0.0 and 2.0, TXMP 3.1, TXRF 1.1, VARS 1.8, VERS 1.2, VERT 1.1, VLay 1.1, VerB 1.1. Proven