Skip to content

Model libraries (MLP, MMP, MGP)

Robomodo RMEGeometryApplies toSHRED

A level’s meshes are not stored one by one. A PAK holds a quartet: four entries with one base name and four extensions, .scp (the scene: placements and more, see Placements), .mlp (the model library: a table of meshes and their metadata), .mmp (the MAIN buffer) and .mgp (the LOCAL buffer). The names of the sections they sit in (MAIN, LOCAL) follow the PS3’s main and video memory, and the split is real: the MMP holds positions and indices and the MGP the other attributes of the same vertices. Observed That the two buffers are literally loaded into main and video memory is inferred from the section names. Inferred

The disc has 657 quartets in 324 of the 336 PAKs, which together hold 21,309 mesh resources and 35,114 submeshes. Proven · all 657 quartets 512 of the quartets hold only rigid meshes; the other 145 (103 character PAKs and 21 layer PAKs) hold meshes with an embedded skinning stream (Skinned streams). Proven · all 657 quartets The 21,309 mesh resources are 17,897 rigid and 3,412 skinned; counts are of occurrences across PAKs, not of distinct meshes. Proven · all 657 quartets

All integers and floats are big-endian. MMP and MGP are raw buffers with no header of their own.

Three bases are in use. Proven · all 657 quartets

  1. The tables of an SCP point at absolute offsets inside the decoded SCP.
  2. A record of the MLP’s resource table gives the offsets and sizes of one mesh’s slices: its metadata is relative to the MLP’s data base, its position and index bytes to the MMP’s start, its attribute bytes to the MGP’s start.
  3. Offsets inside a mesh’s metadata (its stream descriptors and submeshes) are relative to that metadata slice, and the descriptors’ buffer offsets are relative to that mesh’s own MMP or MGP slice.

No relocation table is needed for rigid meshes. The executable’s loader turns the offsets of an SCP and of an MLP into pointers when it loads the file and records that in the u16 at offset 10 (bit 4); a mesh’s metadata carries that bit already set on disc, so it is used as stored. Observed · static reading of the executable

MLP header28 bytesSHRED71% known

Followed by resource_count records of 28 bytes from table_offset, then padding to data_base.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32 = 0xFAFA0003magicProven
0x44u32unknownUnknown0 in the CHARLIE sample
0x82u16 = 0x001Cheader_sizeObservedequals the table offset
0xA2u16load_flagsObserved0 on disc; the loader sets bit 1 for storage it only borrows and bit 4 once relocated
0xC4u32unknownUnknown
0x104u32resource_countProven54 in CHARLIE, 750 in MIKE
0x144u32data_baseProvenwhere the metadata blob starts; 0x610 in CHARLIE
0x184u32 = 28table_offsetProven
MLP resource record28 bytesSHRED100% known

One per mesh. The metadata of a mesh starts at data_base + metadata_offset.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32mesh_idProvenequals the id in the mesh's own metadata at 0x80; placements refer to it
0x44u32metadata_offsetProven
0x84u32mmp_offsetProven
0xC4u32mgp_offsetProven
0x104u32metadata_sizeProven
0x144u32mmp_sizeProven
0x184u32mgp_sizeProven0 when the mesh has no MGP data

The id in the record equals the id inside the mesh in all 21,309 resource occurrences. Proven · all 657 quartets The slices of the MMP and MGP follow one another with zero padding of at most 14 and 12 bytes between them, and in the 512 rigid-only quartets 131,907,800 of the 131,996,256 MMP bytes and 98,794,820 of the 98,833,888 MGP bytes are inside slices; every unconsumed byte is zero. Proven · 512 rigid-only quartets The first CHARLIE record is 296f09da, 0, 0, 0, 0x180, 0x1208, 0x840. Observed

Mesh metadata192 bytesSHRED77% known

Followed at 0xC0 by the submesh records. The descriptors it points at sit after them.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32 = 0xBABA0031magicProven
0x44u32unknownUnknownsentinel
0x82u16 = 0x00C0header_sizeObserved
0xA2u16 = 0xFFFFload_flagsObservedthe loader relocates the offsets at 0x84 and 0x90 to 0xA0 only when bit 4 is clear
0xC4u32unknownUnknownsentinel
0x1064f32[16]local_matrixProventhe identity in all 21,309 resources
0x5016f32[4]bounding_sphereInferredcentre xyz and radius; not needed for decoding
0x6016f32[4]aabb_minProvenlocal box minimum xyz; the fourth value is 1
0x7016f32[4]aabb_maxProvenlocal box maximum xyz; the fourth value is 1
0x804u32mesh_idProven
0x848u32[2]unknownUnknownsentinels
0x8C4u32vertex_format_maskProvenwhich attributes the vertices hold; see Vertex formats
0x904u32primary_streamProvenoffset of the primary stream descriptor
0x944u32attribute_streamProvenoffset of the MGP stream descriptor, or 0xFFFFFFFF
0x984u32index_streamProvenoffset of the index descriptor
0x9C4u32embedded_streamProvenoffset of the skinning stream, or 0xFFFFFFFF for rigid meshes
0xA04u32 = 0xC0submesh_offsetProven
0xA44u32submesh_countProven
0xA84u32secondary_countUnknownmay differ from the submesh count
0xAC8u32[2]runtime_buffersObservedthe loader writes the MMP and MGP base pointers here
0xB44u32flagsObservedbit 0: the skinning stream is already processed (all 3,412 skinned meshes)
0xB88u8[8]unknownUnknown

Every position of every decoded rigid mesh is finite and inside the stored box (to 0.05 units). Proven · all 17,897 rigid meshes

The primary stream (positions and the packed normal, in the MMP) and the MGP stream share a 24-byte layout; the index descriptor has its own.

Stream descriptor24 bytesSHRED67% known
ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32vertex_countProven
0x44u32second_countUnknownequals vertex_count in every sample
0x84u32strideProvenbytes per vertex
0xC4u32unknownUnknownaddress-like or small values; not an offset into the file
0x104u32offsetProveninto this resource's slice of the MMP or MGP
0x144u32sizeProvencount × stride
Index descriptor24 bytesSHRED83% known
ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32index_countProven
0x44u32 = 5primitiveProvena triangle list
0x84u32offsetProveninto this resource's MMP slice
0xC4u32sizeProvenindex_count × 2
0x104u32 = 1index_typeProvenbig-endian u16
0x144u32unknownUnknown0

For a rigid mesh the primary stream is 16 bytes a vertex: three big-endian f32 (x, y, z) and one packed word, the normal (Vertex formats). Proven · all 17,897 rigid meshes The MMP slice holds the vertices then the indices, and the two descriptors together consume it exactly; the MGP stream consumes the MGP slice exactly. Proven · all 17,897 rigid meshes In CHARLIE’s first mesh 176 vertices × 16 = 0xB00 bytes precede 900 indices (0x708 bytes), 0xB00 + 0x708 = 0x1208 = the MMP size, and 300 triangles result. Observed

Submesh record96 bytesSHRED21% known

submesh_count records from submesh_offset; they partition the index buffer.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x016f32[4]bounding_sphereInferred
0x1016u8[16]unknownUnknownaddress-like values
0x204u32first_indexProvenin indices, not bytes; a multiple of 3
0x244u32index_countProvena multiple of 3
0x284u32base_vertexObserved0 in every rigid mesh
0x2C4u32unknownUnknownsentinel
0x304u32unknownUnknown
0x344u32material_idProvena global id looked up in the material banks
0x384u8[4]uv_selectorsProvenwhich stored UV set feeds each logical UV; see Vertex formats
0x3C36u8[36]unknownUnknownpadding and sentinels

The submeshes’ index spans are an exact, disjoint partition of the mesh’s index buffer and every index is below the vertex count. Proven · all 17,897 rigid meshes Every one of the 35,114 submesh occurrences, rigid or skinned, names a material id present in the material banks. Proven · all 657 quartets

What are the unknown words of the metadata and the submesh?

The words at metadata offsets 4, 12, 132, 136, 168 and 184 and submesh offsets 16 to 31, 44, 48 and 60 to 95 hold sentinels, address-like values or zero. Nothing was found that reads them during loading.