Skip to content

Material banks (MBP and MDP)

Robomodo RMEMaterialsApplies toSHRED

A submesh carries a material id, a number that is looked up in a material bank. A bank is a pair of files: an .mbp index and an .mdp of material records. A material names its vertex and pixel shader and its textures by path string, holds the shader’s parameters as float4 values, and lists its texture slots. There are three banks, all in the archive MATERIALS.PAK (in database). Observed

Bank Files Materials
environments environments\env_materials.mbp, .mdp 3,020
characters characters\char_materials.mbp, .mdp 1,065
actors actors\actor_materials.mbp, .mdp 17

All 4,102 records of the three banks were read to their last byte with no byte left over. Proven · all 4,102 materials

All numbers are big-endian.

MBP header24 bytesSHRED100% known

Followed by count entries of 12 bytes, which end exactly at the end of the file.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32 = 0xFADD0001magicProven
0x44u32file_sizeProven
0x84u32 = 0x00180000header_wordsObserveda u16 24 (the table offset) and a zero u16
0xC4u32 = 0xBEEFFACEsentinelProven
0x104u32countProven
0x144u32 = 24table_offsetProven
MBP entry12 bytesSHRED100% known
ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32material_idProvenunique within a bank; 0 occurs in all three banks
0x44u32mdp_offsetProven
0x84u32packed_sizeProventhe byte length in the low 22 bits; the upper 10 bits are 0x004 in every entry

The records start at offset 0 of the MDP, follow one another without gap or overlap and end at its last byte; the id in each record (at 0x10) equals the id of its index entry. Proven · all 4,102 materials

The record starts with a fixed header; every pointer in it is an offset from record + 0x40. After the header come the two shader paths, the parameter descriptors, the parameter values, the sampler descriptors, one texture-path pointer per sampler and the texture paths, padded to 16 bytes with zeros (0 to 15 of them). Proven · all 4,102 materials

MDP record header96 bytesSHRED46% known
ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32 = 0xAAAA0003magicProven
0x44u32 = 0x40header_sizeProven
0x84u32 = 0x00400000unknownUnknown
0xC4u32 = 0xBEEFFACEsentinelProven
0x104u32material_idProven
0x144u32 = 0xFFFF000EunknownUnknown
0x184u32 = 0xFFFFFFFFunknownUnknown
0x1C4u32identifierUnknown3,578 distinct values over 4,102 records
0x208u32[2] = 0xFFFFFFFFunknownUnknown
0x284u32unknownUnknown110 distinct values, 0 to 0x25
0x2C4u32unknownUnknown45 distinct values
0x308u32[2] = 0xFFFFFFFFunknownUnknown
0x384u32zeroUnknown
0x3C4u32state_flagsUnknown34 distinct values; 0x200 in 3,255 records, 0 in 491
0x404u32 = 0xFFFFFFFFunknownUnknown
0x444u32 = 0x20vertex_shader_offsetProventhe path starts at 0x60
0x484u32pixel_shader_offsetProvenpoints just past the vertex shader's NUL; not a length
0x4C4u32parameter_countProven
0x504u32parameter_offsetProvenjust past the pixel shader's NUL
0x544u32sampler_countProven0 to 10
0x584u32sampler_offsetProvenjust past the last parameter value
0x5C4u32texture_pointer_offsetProvenjust past the sampler descriptors

The shader paths are ASCII strings without an extension, the stem of the .shp, .vsp and .psp entries of the SHADERS sections. One material names a pixel program that does not exist on the disc, shaders\pixel\environment\env_challenge_outline. Observed

Parameter descriptor16 bytesSHRED75% known

parameter_count descriptors, then one float4 value for each, in the same order.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32name_hashProvensdbm of the shader uniform's name
0x44u32encodingUnknown0x000100AD in 72,115 descriptors, 0x020100AD in 121
0x84u32 = 0xFFFFFFFFsentinelObserved
0xC4u32value_offsetProvenof a float4 (four f32), right after the descriptors

The materials hold 72,236 parameter descriptors with a float4 each (all finite, inactive lanes included). A hash is matched to a name by taking the sdbm of the uniform names in the material’s own pixel program: 28,121 descriptors match and 44,115 do not. Proven · all 4,102 materials A hash in the same material may repeat; the order is meaningful and is kept. Observed The vertex programs’ own parameters have no names, so a vertex-stage uniform cannot be matched. Observed

Sampler descriptor28 bytesSHRED43% known

sampler_count descriptors, then one u32 offset per sampler to its texture path.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32stateUnknown0 in 30,413 descriptors, 0x01010100 in one, 0x02020200 in seven
0x44u32stateUnknown2 in 30,416 descriptors, 1 in four, 3 in one
0x84u32packed_stateUnknown0x00020C02 in 30,416 descriptors
0xC4u32zeroObserved
0x104u32binding_hashObserved21 distinct values; four are named, see below
0x144u32stateUnknown0x200 in 30,408 descriptors; 0, 0x201, 0x202 and 0x300 in the rest
0x184u32 = 0xFFFF000EsentinelObserved

The texture path of a sampler is the name of a .thp entry without its extension. Of the 30,421 sampler slots, 15,319 have an empty path, 14,117 name a texture that exists on the disc (case-insensitively) and 985 name one that does not (91 in the character bank, 894 in the environment bank). Proven · all 4,102 materials A material’s textures are therefore not all present: a reader has to keep an unresolved path as it is. Observed

The binding hash is an sdbm. Four of the 21 values equal the sdbm of a string in the executable: 95591c40 is Color_Map (3,995 slots), d79d6be4 Normal_Map (3,948), ffc34d98 Spec_Map (2,998) and 2538db2e Effect_Map (2,816), 13,757 slots in all. The other 17 values (16,664 slots) match no string in the executable, the scripts or the shader files. Proven · 2,661,053 candidate strings That a slot is named Color_Map shows the identity of the slot, not what the pixel program does with it. The shaders’ parameter tables list no samplers, so which texture unit a slot feeds is not established, and “slot i feeds unit i” is not assumed. Unknown

A submesh’s id is looked up in the bank of its own quartet’s namespace: the path of the quartet decides. Every nonzero id under levels\ is found in the environment bank, under characters\ in the character bank and under actors\ in the actor bank, and none is missing. The 34,469 nonzero references (2,885 distinct ids) are 26,669 environment, 1,521 character and 6,279 actor. Proven · all 35,114 submeshes A PAK’s directory does not decide: level PAKs hold both environment and actor materials. Proven · all 35,114 submeshes

Material id 0 occurs in all three banks, and 645 submeshes use it (514 in levels, 5 in characters, 126 in actors). The namespace rule picks the bank for it as well, which is reasoning, not something the loader was seen to do. Inferred With that rule the 35,114 submeshes use 27,183 environment, 1,526 character and 6,405 actor materials. Inferred

How does the engine choose between the banks?

The loader’s lookup (the function at 0x00415b84 that the model finalizer calls with the submesh’s id) was not traced to a bank choice.

What do the state words mean?

The header words at 28, 40, 44 and 60 and the sampler state words are kept as integers; their values (blend, depth and sampler state are the obvious candidates) are not tied to anything in the engine.