ShaderBlockDepots and material parameters
A MeshSet section’s 64-bit material key resolves in ShaderBlockDepots (0x73312045) of loaded bundles.
The depot contains parameters, not executable shaders. A cell depot requires no explicit EBX reference to be
registered: membership in the loaded bundle is sufficient. This is verified by moving depots into cell bundles
and observing correct texture binding in game. Observed
Depot container
Section titled “Depot container”The container is little-endian. Eight measured depots, including Studio, stock cell and terrain/compositor examples, reproduce byte for byte after decoding and re-encoding. Observed
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 4 | u32 | payload size before relocations | Observed | |
| 0x4 | 4 | u32 | relocation byte count | Observed | |
| 0x8 | 4 | u32 | unknown tag | Unknown | 0x01190003 in all eight measured depots; interpreted as version by readers |
| 0xC | 4 | u32 | key count | Observed |
The payload begins with two u64 words, 0x10 and zero, then a table of key/descriptor-pointer pairs. Parameter
blocks are packed in first-use order, followed by 16-byte alignment and their descriptors. After another
alignment, a zero-terminated list of u32 offsets identifies pointer slots to relocate. Several keys can share
one descriptor; the key table is not necessarily sorted. Observed
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 8 | u64 | material key | Observed | |
| 0x8 | 8 | u64 | descriptor file offset | Observed |
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 8 | u64 | block data file offset | Observed | |
| 0x8 | 8 | u64 | block hash | Observed | |
| 0x10 | 8 | u64 | block byte count | Observed |
Parameter blocks
Section titled “Parameter blocks”A block starts with u32 parameter_count. Every parameter begins with the record below. All blocks of the eight
measured depots parse with no bytes left over. Observed
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 8 | u64 | key hash | Observed | The top 16 bits are the low half of the 32-bit name hash; the 48 bits below are not explained (see Parameter keys). |
| 0x8 | 4 | u32 | type hash | Observed | |
| 0xC | 2 | u16 | count | Proven | Equal to the u32 at +16 in every record that has one |
| 0xE | 2 | u16 | identity | Observed | The high half of the 32-bit name hash; checked on the 207 keys whose parameter asset is known |
The 2,473 distinct stock depots hold 192,642 material entries; entries of one depot that share a descriptor share a
block, so they point at 64,105 parameter blocks (1,762,286 parameter records; 5,451,023 counted once per entry).
Counted once per entry, the u16 at +12 equals the u32 at +16 in all 4,527,851 non-texture records, 633,249 of which
hold more than one element, and each record’s length is 20 plus that count times the element size; texture records have
count 1 (923,172). The count of a value is therefore an element count, not a byte size. Proven · every stock block
732 entries, which use 95 blocks, hold a parameter of a type that is not in the table below (0x566e2dd0, one byte an
element; 95 records); with that size every one of the 64,105 blocks parses to its last byte. Proven · every stock block
The 1,636 distinct 64-bit key hashes of the stock depots, none of them used under two parameter types, have 1,636
distinct low 48 bits and 1,609 distinct top 16 bits, and the u16 at +14 is the same for every record with the same key
hash. Proven · every stock block
The top 16 bits of the key hash and the u16 at +14 are the two halves of one 32-bit name hash (see
Parameter keys). The u16 takes 1,337 distinct values over the 1,636 key hashes, where independent
16-bit values would give about 1,616; 1,010 pairs of key hashes share a value against about 20 expected, one value is
shared by 16 key hashes, and the 334 texture parameters have only 239 distinct values. Proven · every stock block The
excess is the hash’s: it is a djb2-style hash, whose high half does not change with the last one or two bytes of a name,
so parameters whose names differ only there (param_region_color_0a and param_region_color_0b, param_basecharacter_isk
and param_basecharacter_utk) share the u16. Of the 182 values the 207 known keys take, 21 are shared, each by names that
differ only in their last characters. Observed · 207 keys with a known parameter asset
The value is not a function of the type, of the top 16 bits of the key hash (the 27 pairs of hashes with equal top 16 bits
have different values), or of any 16-bit slice of the 64-bit hash or the xor or sum of the four slices.
Observed · every stock block
Seeded with 5381 (the game’s asset-name hash), the djb2-XOR hash of none of the 303,105 distinct strings found in the stock
EBX documents forms the top 16 bits together with the u16 at +14, and neither is any reflection name key (21,561 distinct
keys); the seed is 0x6875ca4d. Observed · 303,105 strings; 1,636 key hashes
None of the 1,636 key hashes occurs as a constant in Skate.exe. Proven · every 8-byte window of the executable image
Texture type 0x14c5fc30 carries one flag byte, then the TextureAsset instance GUID and EBX file GUID, in that
order. All 2,038 measured texture flags equal one. Texture binding uses these GUIDs rather than the Texture
resource id or name; 402 of 402 sampled map bindings resolve in this order. Observed
Other types begin their value with another u32 count, equal to the prefix count in the measured blocks, followed
by fixed-width elements. Observed
| Type hash | Element interpretation | Bytes | Confidence |
|---|---|---|---|
0x2bb65c2d |
float | 4 | Observed |
0x36ed3444, 0x8e4a817e, 0x7826e998 |
Integer/boolean/enum representations | 4 | Observed |
0x57f970ca |
Packed colour | 4 | Observed |
0x2a36e9d5 |
float2 | 8 | Observed |
0x885eff59 |
float3 | 12 | Observed |
0x7f999829 |
float4 | 16 | Observed |
0x566e2dd0 |
One-byte value (boolean) | 1 | Observed |
0x14c5fc30 |
Texture reference (49-byte record) | Observed |
Two of these type ids are the reflection name keys of types in the stock EBX documents: 0x2a36e9d5 is the key of the
type name Vec2 and 0x7826e998 the key of WpsDistance; the other eight ids are the key of no name in those documents.
The parameter type id therefore comes from the same case-insensitive name hash as the
reflection name keys. Observed · 21,561 keys against 10 type ids
Parameter keys
Section titled “Parameter keys”A shader parameter’s identity is a 16-byte key: the prefix of every depot parameter record is a key, and EBX documents
carry the same 16 bytes as the Data GUID of the ParamDbKeySerialized structure. A material’s parameter node in the
running game holds it with the identity word first: u16 identity, u16 size, u16 type index, u16 count, u64 hash.
Sourced · ReSkate
The name hash. The 32-bit name hash of a parameter is a lower-cased djb2-XOR hash of the document name of its
ShaderExpressionParameter asset, started from 0x6875ca4d:
h = 0x6875ca4dfor c in lower(name): h = (h * 33) ^ c (32-bit arithmetic)key hash bits 48..63 = h & 0xffff identity (bytes 14..15) = h >> 16All 207 distinct keys that the stock EBX documents tie to a parameter asset satisfy it, with the asset’s document name as the
bundle manifests state it (lower case; the asset’s own Name field holds the same path in its original case).
Proven · all 207 keys tied to an asset in the 328,015 stock payloads
Names that differ only in their last byte have name hashes that differ by the XOR of the two bytes (20 pairs, for example
param_region_color_2a and param_region_color_2b: 3). Observed · 20 pairs of known names
The 207 keys are 40,262 uses by AppearanceExpressionParamInfo values, whose Value.Parameter pointer names the asset;
each key has one asset and each asset one key. 195 of the 207 keys equal a depot parameter prefix in hash, type and
identity. Proven · every stock payload and depot
Of all 330,760 document and resource names of the install, exactly those 195 hash to the name hash of a depot parameter, and
of the 1,216,438 distinct strings of the stock documents only one other does, by coincidence (riff 1): the names of the
other 1,441 depot key hashes are stored nowhere as a document name or a string value. Proven · every manifest name and EBX string
The seed is a measured constant; how the game arrives at it is not known. Unknown
It is not the djb2-XOR hash, started from 5381, of any string of up to six characters from a-z, 0-9 and _/. -, of the
157,722 strings of the executable and its reflection tables, or of the 330,760 document and resource names with their
directory prefixes and path components, and it is not written in either of the two authoring executables.
Observed · the strings and executables named
FrostyToolsuite’s profile describes the top 16 bits as the low half of this hash of the lower-cased name Sourced · FrostyToolsuite;
for skate. the census confirms it, with the identity word as the high half and this seed.
The assets. The 212 documents under common/shadercomponents (202 under parameters, 10 under configuration) are 203
ShaderExpressionParameter assets, each a single Name field holding the document’s path, and nine enumeration type assets.
All 203 parameter assets have a key; the enumerations have none. Four more keys belong to assets in shaders/character/body/_sg
(param_coloroffset_mult2x, param_coloroffset_saturation, param_skinindexasfloat,
sg_body_applyskindetail/detailnormalintensity). Proven · all 328,015 stock payloads
Where documents carry keys. 381 distinct non-zero keys occur in 9 structure types of the stock documents; 357 of them are depot parameter hashes and 207 are tied to an asset. Proven · all 328,015 stock payloads
| Structure | Uses | Documents | Beside the key |
|---|---|---|---|
AppearanceExpressionParamInfo.ParamKey |
40,262 | 1,679 | Value.Parameter, a pointer to the parameter asset |
TextureCompositorSerializedParameterValue.Parameter |
1,110 | 579 | nothing (8 keys) |
ShaderParameterBlockEntryData.Key |
525 | 76 | nothing (124 keys) |
DingoCustomizationItemColorShiftRuntimeColor.Key |
228 | 39 | colour positions (16 keys) |
EffectParameter.ShaderParamKey |
191 | 4 | Name; one use has a key, the other 190 are zero |
LuaShaderParamBindingRuntimeEntry.Key |
34 | 5 | Name, the alias a Lua script uses (30 keys) |
DingoAdTechComponentData.ParamKey |
20 | 7 | ParamName, a display name such as [C] (Mutable) (1 key) |
AppearanceExpressionShaderParameterPipelineData.Key |
16 | 3 | nothing (12 keys) |
ShaderParamsComponentData.ParameterKey |
57 | 55 | ParameterName (NightAmount); the key is zero |
The Name beside a key is not always the asset’s name: the Lua bindings map aliases to keys (Base_C_C and
Param_Composite_C name one key; BodyNormalMap names the key of param_basecharacter_ny and HeadNormalMap that of
param_facebase_ny), and a display name need not hash to its key. Observed · the 34 Lua entries
A key with a zero value and a name beside it (all but one EffectParameter, and NightAmount in the visual environment’s
ShaderParamsComponentData) is one the game fills in when the document loads, from the name. Inferred
The name hash of Param_Composite_* Lua names back-solves to the directory common/shadercomponents/parameters/composite/
(param_composite_c, _ny, _ogk, _osk, _vibrancy, param_composite_boardwear_intensity_{body,nose,tail}_h): no document
has these names, yet their keys carry the name hashes of those paths; the three _w variants do not back-solve to it.
Observed · 11 names
Name groups. Of the 1,441 depot key hashes whose names are unknown, 319 fall into 94 groups that share the upper 24 bits of
the 32-bit name hash, which is what names differing only in their last character give; random 32-bit values would give 0.06 such
pairs, and the 207 known names have 20 such groups of 2 members. Observed · 1,441 unnamed and 207 named keys
Candidate names for nine of these name hashes, each found by a 32-bit match only (the low 48 bits of the keys cannot confirm
them): the stems param_hair_breakupwarp, param_hair_breakupweights, param_hair_breakupflattargetnoise and
param_hair_translucency, each with a final 0 and 1 (eight hashes, four of the groups), under
common/shadercomponents/parameters/character/hair/regions/, where assets param_hair_breakupwarpmain and three more with the
same stems exist; and common/shadercomponents/parameters/regions/region2/param_region_breakupenum_2a, the sibling of the
existing …_2 and …_2b. Inferred · 9 name hashes
The chance odds: the search for the four stems compared about 10.7 million candidate names (directory, word and prefix
combinations) with the 2,290 prefix states of the 94 groups, which gives about 6 chance matches; it found 9, five of them
meaningless names (for example …/skin/param_debug_haircap), so a match alone proves nothing, and these four are credible
only because each is a real word of the right directory next to assets with the same stem and each match explains a pair
of hashes that differ in the last character. The …_2a match came from 283,383 candidates (two characters of a known name
replaced) against 1,441 hashes, which gives 0.1 chance matches in all. Inferred · 9 name hashes
Parameter collections. Nine resources of type 0xf434940d (three in the globals TOC, six in the dingolevel_root TOC; for
example common/shadercomponents/configuration/collections/characterglobal_collection and worldsurfacing_collection) list
the keys of a parameter group, 16 bytes each in the layout of the prefix above, followed by pairs of (byte width, offset) that
place the values. Observed · 2 of the 9
Material families
Section titled “Material families”Measured stock world-object materials use a two-set template with a 70-parameter, 2,426-byte block. The top 16 bits of
the primary colour, mask and normal texture parameters’ key hashes are 79a3, 9695 and af17, the name-hash halves of
param_base_c, param_base_msk and param_base_ny; those of the secondary colour, normal and mask parameters are
ba61, 0795 and 0fd7 (their names are not known), alongside grime and texel detail inputs. Observed
Mask channels are R metallic, G smoothness and A opacity. B is a per-material constant in the measured data, but its meaning is unresolved. Observed
The u51.x vertex value selects the secondary set for value 1; sampled values 0, 2, 3, 4 use primary.
The selection is flat per triangle. Color0 does not interpolate between these texture sets; it only shades the
primary set slightly. This is measured in game on the two-set row (413, 2332, 2608, 3000), which has no per-vertex
blend between the sets. Observed
The 48 bits of a key hash below the name hash’s low half are not the CityHash64, FarmHash, xxHash, Murmur3 or FNV hash
(64-bit and 128-bit forms, several seeds) of a parameter asset’s name, its lower-cased, upper-cased or UTF-16 form, its last
path component, or any of several million buffers that join the name with its type id, name hash, length (1, 2, 4 or 8
bytes), a NUL, or the layout FrostyToolsuite’s profile describes (a 1, three lengths, name, type, module) with any of 21,621
reflection names as the type and 4,080 directory strings as the module, or 1.24 billion CityHash64 buffers of that layout
with 35 type names and 246,482 executable, reflection and directory strings as the module. The 48 bits are not bytes of
the parameter asset’s file GUID or of its instance GUID (any six-byte window, either byte order), and over the 207 keys they
look uniformly random (bit balance chi-square 47.7 on 48 degrees of freedom, mean pairwise Hamming distance 24.0 of 48).
Observed · 207 keys A stored random value rather than a function of the name would fit this. Inferred
In practice a writer never needs the function: a stock parameter’s key is copied from the stock material or depot, never
recomputed, and a new parameter’s key is its name hash plus any 48 bits, provided the material and the depot written beside
it carry the same key. Inferred · the engine does not hash names while a level loads; not tried in the game
The engine holds a CityHash64 equal to the
reference implementation on inputs of 0 to 1,000 bytes, and none of its 264,603 functions of up to 2,560 bytes returns the
key hash, its low 48 bits or its high 48 bits from a parameter asset’s name (exact case, lower case or last component) in
nine argument layouts under emulation; the same sweep finds the engine’s CityHash64 when asked for CityHash64 of the name.
Observed · the 203 parameter assets of known name; the executable image
In a stock launch to the hub (two launches, about 150 s each, hardware execution breakpoints on the engine’s CityHash64 at
+0x547fcc0, its seeded wrapper at +0x54801c0 and its generic byte hasher at +0x379c310), none of the three ran once,
while a control function ran 310 times: the engine computes no parameter key from a name through them while a level loads.
Observed · 2 launches
After the hub has loaded, 6.6 GB of private memory holds 10,759 to 12,444 depot parameter records (638 to 712 of the 1,636 key
hashes) and about 69,000 more occurrences of 1,550 key hashes, in the material parameter nodes (hash at +8 of a node);
none of the strings within two pointer hops of the first 60,000 of them is a parameter name. Observed · 2 launches
Block hashes agree for equal measured blocks but do not match tested FNV, MD5 or SHA-1 representations; they are a separate
identity. Unknown