BLANCO

Developer · blancodagoat.dev

← All parts

CLOTHING PACKS

GTA V / RAGE file formats › Part 5

Add-on freemode clothing end to end: pack shape, the drawable and texture naming grammar, CPedVariationInfo and propInfo, texture and UV rules, engine limits per build, merging packs, and the CodeWalker .ydd.xml a converter must emit.

How to read the evidence markers on this page: no marker — verified from source or a primary spec, trust it. 🟡 — community reverse-engineering, widely used but never confirmed by Rockstar. 🔴 — folklore: repeated in tutorials, plausible, untested — verify before relying on it. ✅ — confirmed in-game on a real working pack. Full legend and corrections log.

16. Clothing DLCs (add-on, freemode)

Derived 2026-08-04/05 from in-game-running community add-on packs (mpClothes-style) cross-checked against vanilla mpbeach/mpbusiness2, with byte-level round-trips through CodeWalker.Core. ✅ tier: the structure below matches packs verified running in-game on this machine.

Pack shape

dlc.rpf                    (OPEN encryption -  no keys involved)
├─ content.xml             SHOP_PED_APPAREL_META_FILE + RPF_FILE entries,
│                          both enabled by ONE <NAME>_GEN changeset
├─ setup2.xml              EXTRACONTENT_COMPAT_PACK, GROUP_STARTUP, order 999,
│                          isLevelPack=false, deviceName dlc_<name>
├─ common/data/mp_m_freemode_01_<name>.meta    ShopPedApparel
└─ x64/models/cdimages/<name>_male.rpf         (OPEN child rpf)
   ├─ mp_m_freemode_01_<name>.ymt              CPedVariationInfo
   └─ mp_m_freemode_01_<name>/                 the drawables + textures

Female is symmetric: mp_f_freemode_01, <name>_female.rpf, eCharacter SCR_CHAR_MULTIPLAYER_F (male: SCR_CHAR_MULTIPLAYER). An empty <creatureMetaData /> is valid: vanilla mpbusiness2 ships one; community packs often carry a dangling reference, which the game tolerates. Empty pedOutfits/pedComponents/pedProps lists are valid too.

File naming grammar

KindPatternExample
Component drawable<comp>_<nnn>_<flag>.yddjbib_000_u.ydd
Component texture<comp>_diff_<nnn>_<letter>_<race>.ytdjbib_diff_000_a_uni.ytd
Prop drawablep_<anchor>_<nnn>.yddp_head_000.ydd
Prop texturep_<anchor>_diff_<nnn>_<letter>.ytd (no race suffix)p_head_diff_000_a.ytd

The 12 components, in PV slot order: head berd hair uppr lowr hand feet teef accs task decl jbib. Prop anchors, in enum order (= anchorId): head eyes ears mouth lhand rhand lwrist rwrist hip lfoot rfoot. Numbering must be dense from 000 per component / per anchor.

Naming law: in 400 vanilla clothing ydds + 400 ytds sampled, the internal dictionary / texture names matched the filename in 100% of files. The game keys clothing by filename; renaming files without re-keying internals is universal community practice and what running packs do.

CPedVariationInfo (.ymt)

Lives inside the cdimages rpf, next to the ped folder. Buildable from XML via XmlMeta.GetData(doc, MetaFormat.RSC, ""), which round-trips XML-exact against real packs, and the output carries the RSC7 header RpfFile.CreateFile needs to classify it as a resource.

  • availComp: 12 slots in the component order above; value = dense index into aComponentData3 for comps present, 255 otherwise.
  • Per drawable (CPVDrawblData): propMask 1, numAlternatives 0, one aTexData item per texture (texId 0, distribution 255), clothData.ownsCloth = whether a .yld ships.
  • compInfos: one CComponentInfo per drawable (pedXml_compIdx = slot, pedXml_drawblIdx = number, audio ids none, zeros elsewhere).
  • dlcName = JOAAT of the pack name.

propInfo (props in the same ymt)

  • Per prop (CPedPropMetaData): texData items with incrementing texId 0,1,2… (unlike components), anchorId = anchor enum index, propId = per-anchor ordinal.
  • propFlags 65569 for head props (hat flags, plus expressionMods "-0.5 0 0 0 0" for the hair squash); 65536 otherwise; renderFlags PRF_ALPHA on eyes (glasses).
  • aAnchors: per anchor with props, <props> = space-separated per-prop texture counts, <anchor> = enum name (ANCHOR_HEAD, …).

Textures & UVs

  • Clothing diffuse .ytd: one DXT5 texture, full mip chain to 1×1 (≥9 levels → sources ≥256, power-of-two, ≤4096).
  • Clothing UVs are commonly negative (V in [-1,0]) and rely on GPU wrap addressing. D3D and CodeWalker wrap by default. A renderer that clamps (three.js default) smears the edge texels across the mesh; set repeat wrapping.
  • DXT encoder gotcha: naive 565 truncation green-shifts dark greys ((4,4,4) → (0,4,0)). Round-to-nearest with a neutrality bias on near-achromatic colors fixes it.

Limits

The engine loads a fixed number of variation .ymt files per gender: about 80 before b2612 (mpg9ec), ~100 after. The ceiling matters less than the slots Rockstar has left, which shrinks with every Online DLC:

BuildDLCFree ymt slots (per gender)
b2612mpg9ec~20
b3095mp2023_02~10
b3407mp2024_02~6
b3570mp2025_01~5
b3717mp2025_02~4
b3889mp2026_01~3

"Three free slots" means three for male and three for female. The budget is per gender, not shared. A pack with male and female clothing is two ymts; add high heels or head-prop hair modifiers and it also emits a shared mp_creaturemetadata_<name>.ymt, making three. If a pack uses cut-hairs or remove-hairs head props and no creaturemetadata ymt was built, the hair modifier silently does nothing.

  • 128 drawables per component per gender is a legacy synchronization boundary, not a file-format cap. FiveM builds carrying the component-sync patch accept component IDs 0-255 (client and server both need it); props keep the 128 advisory because that patch doesn't cover prop sync. Past 255 is the real collection boundary.
  • 255 global prop index: older runtimes hold part of the global prop index in 8 bits, shared with base-game and DLC props. Past it, addon props turn invisible rather than crash. Patched on FiveM; singleplayer needs the pedprop limit-adjuster ASI. It is not a 255-item cap inside one collection.
  • High heels above global component index 255 hit the same truncation class in the component-expression lookup, so heel height silently stops applying. Patched on FiveM.
  • TxdStore Pool Full, Size == 95500 during INIT_SESSION for CExtraContentWrapper means too many .ytd files are loading across all mods, not just clothing. Tattoo packs weigh heavily since they're ytd-based.

Limit figures in this subsection come from the Durty Cloth Tool documentation (read 2026-08-07), not from our own testing, unlike the pack structure above, which is byte-verified here. Rockstar changes these every update; check the source for current builds.

The DLC Builder's Variant Splitter and Add-on Pack tools implement all of the above client-side.


17. Merging DLC packs

Derived 2026-08-07 from two in-the-wild community packs (a helmet prop pack and a tattoo pack) merged and byte-verified through CodeWalker.Core. ✅ tier for structure; the in-game load test of a merged pack is still open.

Why merge at all

The usual reason isn't saving dlcpacks slots. It's removing conflicts. Add-on packs patch vanilla archives by name. If one pack overlays mpstunt for clothes and another overlays it for tattoos, installing both means only one loads; the other silently doesn't appear. Combining them into a single pack lets both sets of files coexist and load together.

Community build tools also tend to emit the same device name and changeset name for every pack they produce (dlc_pck / pck_AUTOGEN is common), so two such packs collide on identity as well as content.

What a merge has to do

  • Copy file payloads verbatim. Only content.xml and setup2.xml are regenerated.
  • Rewrite the device prefix. Every dlc_<old>:/ path in dataFiles and changesets becomes dlc_<merged>:/.
  • Keep one changeset per source pack, renamed so two pck_AUTOGENs don't collide. A duplicate changeset name means one pack's enable list is shadowed and its content never loads.
  • Union colliding nested rpfs. If both packs ship mpstunt_ped_mp_overlay_txds.rpf, rebuild it holding both packs' entries. Picking a winner would reproduce the very conflict the merge is meant to fix.
  • Union the metadata that declares them. Unioning texture archives isn't enough if the file listing the entries gets overwritten. A pack overriding common/data/effects/peds/multiplayer_overlays.xml ships the entire vanilla <presets> list plus its own additions, so two tattoo packs reconcile by unioning those items keyed on <nameHash>: shared vanilla entries dedupe, each pack's custom tattoos survive. Only a genuine same-nameHash clash needs a winner.
  • Re-add the RSC7 header when copying resources. Extracting an RpfResourceFileEntry yields headerless pages; writing those back produces a binary entry, and the game dies with ERR_SYS_INVALIDRESOURCE_5 at stream time even though the payload bytes are identical.
  • Leave nested rpfs uncompressed. The game mounts them by seeking to their offset, so a deflated child archive won't mount.

Things that look wrong but aren't

  • The output is much smaller than the inputs. RPF doesn't reclaim space when entries are replaced or deleted. Freed blocks stay allocated until the archive is defragmented, so packs edited in OpenIV carry dead space. One real 391 MB tattoo pack held ~200 KB of live content; 547 MB of input merged down to 91 MB. Writing a fresh entry table reclaims the slack.
  • A dataFiles entry pointing at a file the pack doesn't ship. Real packs do this and leave it disabled; it's inert. Only an enabled missing file is a problem.

What merging does not fix

Merging clothing packs saves a dlcpacks slot, not a ymt slot: each source pack's .ymt is carried across as-is, so two merged clothing packs still consume two ymt slots per gender. Given ~3 free slots on b3889, that ceiling is usually the real constraint. Getting under it needs true CPedVariationInfo merging with drawable renumbering, which is a different job.

List-shaped metadata is only unioned for formats a merger actually understands. Ours currently knows PedDecorationCollection (tattoos/decorations) and nothing else. Anything unrecognised has to fall back to last-writer-wins and say so, rather than guess at a structure it can't validate and silently corrupt it.

The DLC Builder's DLC Merger implements the rules above client-side.


27. CodeWalker .ydd.xml (the clothing import path)

To get a new skinned clothing mesh into the game, emit CodeWalker's XML form of a DrawableDictionary and import it (CodeWalker or Sollumz → .ydd). The schema below imports cleanly and was reverse-engineered from a real CodeWalker export of a freemode jacket (jbib_000_u). The XML has no published spec, so the only true validation is a CodeWalker import.

Top-level structure

<?xml version="1.0" encoding="UTF-8"?>
<DrawableDictionary>
 <Item>                              <!-- one per drawable; clothing = 1 -->
  <Name>jbib_000_u</Name>
  <BoundingSphereCenter x=".." y=".." z=".." />
  <BoundingSphereRadius value=".." />
  <BoundingBoxMin x=".." y=".." z=".." />
  <BoundingBoxMax x=".." y=".." z=".." />
  <LodDistHigh value="9998" /> <LodDistMed value="9998" />
  <LodDistLow value="9998" />  <LodDistVlow value="9998" />
  <FlagsHigh value="1" /> <FlagsMed value="0" /> <FlagsLow value="0" /> <FlagsVlow value="0" />
  <ShaderGroup> ... </ShaderGroup>
  <DrawableModelsHigh> ... </DrawableModelsHigh>   <!-- High alone imports and previews -->
  <Lights />
 </Item>
</DrawableDictionary>

Bounds are computed from all vertex positions. No <Skeleton> block for the common clothing case: the garment binds to the ped's external skeleton (from mp_*_freemode_01.yft), and the rig lives entirely in per-geometry <BoneIDs> plus per-vertex BlendIndices. A dictionary drawable can embed its own skeleton (some ped components do), but a writer targeting freemode clothing omits it. RAGE caps skin influences at 4 per vertex.

ShaderGroup

A minimal ped shader imports: <Name>ped</Name> <FileName>ped.sps</FileName> <RenderBucket value="0" />, texture parameters DiffuseSampler / BumpSampler / SpecSampler (plus VolumeSampler), and the usual vector parameters (bumpiness, specularIntensityMult, specularFalloffMult, specularFresnel, umGlobalParams, envEffFatThickness, …). The embedded <TextureDictionary> can carry normal/spec entries, or be an empty <TextureDictionary /> for a first pass with the diffuse shipped in a separate .ytd.

Model and geometry

<DrawableModelsHigh>
 <Item>
  <RenderMask value="255" /> <Flags value="1" />
  <HasSkin value="1" />              <!-- REQUIRED for clothing -->
  <BoneIndex value="0" /> <Unknown1 value="128" />
  <Geometries>
   <Item>
    <ShaderIndex value="0" />
    <BoundingBoxMin x=".." y=".." z=".." w="0" />
    <BoundingBoxMax x=".." y=".." z=".." w="0" />
    <BoneIDs>0, 1, 2, ... 127</BoneIDs>   <!-- remap table; BlendIndices index INTO it -->
    <VertexBuffer>
     <Flags value="0" />
     <Layout type="GTAV1">
      <Position /> <BlendWeights /> <BlendIndices /> <Normal />
      <Colour0 /> <Colour1 /> <TexCoord0 /> <TexCoord1 /> <Tangent />
     </Layout>
     <Data2> ...one row per vertex, columns in Layout order... </Data2>
    </VertexBuffer>
    <IndexBuffer><Data> ...triangle indices, whitespace-separated... </Data></IndexBuffer>
   </Item>
  </Geometries>
 </Item>
</DrawableModelsHigh>

Two details are easy to miss. Skinned meshes put vertex rows in <Data2>, not <Data>, and <BoneIDs> is a remap table: BlendIndices values index into that list, not into the skeleton directly (an identity list 0..N works).

GTAV1 vertex row

Px Py Pz   W0 W1 W2 W3   I0 I1 I2 I3   Nx Ny Nz   C0r C0g C0b C0a   C1r C1g C1b C1a   U0 V0   U1 V1   Tx Ty Tz Tw
Column groupTypeNotes
Positionfloat3model space
BlendWeights4 × bytemust sum to 255
BlendIndices4 × byteindex into <BoneIDs>
Normalfloat3unit length
Colour0 / Colour14 × byte eachobserved defaults on a real jacket: 255 255 255 255 and 255 255 255 0; these feed lighting/wind, replicate observed values unless you know better
TexCoord0float2V stored negated (e.g. -0.7578), the same convention as the binary (§18)
TexCoord1float2often a copy of TexCoord0, or zeros
Tangentfloat4xyz + handedness w (usually 1); compute from positions + UVs + normals if the source mesh has none

A converter starting from a bare mesh (positions/UVs/indices/weights) must synthesize the rest: normals (from the source or per-face-averaged), tangents (per-triangle accumulation over UVs, normalized, w = handedness), Colour0/1 defaults as above, TexCoord1 = TexCoord0, BoneIDs = identity. High-LOD-only imports and previews fine; in-game use wants Med/Low too.

Provenance: schema read from a real CodeWalker export of a running freemode jacket and cross-checked against Sollumz's format handling; import-validated through the CodeWalker GUI, which remains the only authoritative acceptance test.