3. The .y* resource family
All are RSC7 resource files: a 16-byte header plus a system (CPU) page and graphics (GPU) page, page-mapped, deflate-compressed.
RSC7 header:
u32 ident 0x37435352 ("RSC7")
u32 version resource type version (the table below)
u32 systemFlags page layout, system segment
u32 gfxFlags page layout, graphics segment
Page sizes are packed into the flags words as a base size (0x200 << (flags & 0xF)) times a sum of bit-weighted page counts (see RpfResourceFileEntry.GetSizeFromFlags()) if you need to compute them.
On the y prefix: the old claim that y means "win64/PC, consoles use x/w/c" is wrong. That's the GTA IV scheme (wdr = Windows, xdr = Xbox 360, cdr = PS3/Cell). GTA V uses .ydr on every platform. The y is just what RAGE moved to; it does not encode a platform.
For completeness, the full RAGE resource-extension prefix scheme (the first letter of an extension like ydr/ymap, across platforms and RAGE titles). GTA V ships everything under y:
| Prefix | Platform |
|---|---|
a | android |
c | ps3/psn |
d | xboxone/durango |
i | independent (North-specific) |
n | nx64 |
o | ps4/orbis |
p | ps5/prospero |
s | ios |
v | vita/psp2 |
w | win32/x86/pc |
x | xbox360/xenon |
y | win64/x64/linux (GTA V) |
z | xbox series x/s |
| Ext | Resource | Version | What it is |
|---|---|---|---|
.ydr |
Drawable | 165 | One model. Geometry, shaders, skeleton, optional embedded collision + LODs |
.ydd |
Drawable Dictionary | 165 | Many drawables in one file (ped components, prop sets) |
.yft |
Fragment | 162 | Breakable/physics object. Vehicles, destructible props. Drawable + damage/physics LODs |
.ytd |
Texture Dictionary | 13 | DDS textures (DXT1/3/5, BC7, A8R8G8B8) + mipmaps |
.ybn |
Static Bounds | 43 | Standalone collision |
.ymap |
Map Data | 2 | Placement: what goes where |
.ytyp |
Archetype Def | 2 | Definition: what a thing is |
.ynv |
NavMesh | 2 | Ped pathfinding |
.ynd |
Path Nodes | 1 | Vehicle path nodes |
.yvr |
Vehicle Record | 1 | Recorded vehicle paths |
.ywr |
Waypoint Record | 1 | Ped/vehicle waypoint routes |
.ycd |
Clip Dictionary | 46 | Animations |
.yed |
Expression Dict | 25 | Procedural bone expressions |
.yld |
Cloth Dictionary | 10 | Cloth simulation data |
.ypt |
Particle Effects | 68 | PTFX assets |
.ymf |
Manifest | 2 | ytyp↔ymap dependency graph (_manifest.ymf) |
.ymt |
Metadata | varies | PSO-encoded metadata |
.ysc |
Script | N/A | Compiled RAGE script |
.gxt2 |
Text | N/A | Localized strings |
.awc |
Audio Wave | N/A | Audio container |
.dat |
Various | N/A | cacheloader, gtxd, handling, .rel audio |
ytyp vs ymap: the core distinction
- ytyp = the class. "There exists an object
prop_bench_01a, model X, bounds Y, LOD distance Z." - ymap = the instances. "Place
prop_bench_01aat (150, -300, 20) rotated 45°."
You always need both. The ytyp must be loaded (DLC_ITYP_REQUEST or PERMANENT_ITYP_FILE) before any ymap referencing it.
18. Drawable & texture internals (.ydd / .ydr / .ytd)
§3 catalogues the .y* family; this section is the byte-level walk for the three you touch when editing clothing and textures. ✅ tier throughout: every struct offset below was confirmed against real GTA V files (freemode clothing .ydds and their .ytds) on this machine, and the parser that uses them round-trips those files under unit test.
RSC7 virtual pointers
Inside a decompressed resource, structures reference each other by absolute virtual address, not file offset. The address space is segmented:
| Pointer looks like | Lives in | Resolve |
|---|---|---|
0x5xxxxxxx | system (CPU) segment | ptr & 0x0FFFFFFF = offset into system segment |
0x6xxxxxxx | graphics (GPU) segment | ptr & 0x0FFFFFFF = offset into graphics segment |
Pointers are stored as u64 but the address always fits in the low u32. The decompressed payload is [system segment | graphics segment] back to back. Segment sizes come from the header's two flag words: a 0x200 << (flags & 0xF) base page size times a bit-weighted page-count sum (see the RSC7 header in §3).
A .y* file must be stored in the RPF as a resource entry, with its header intact. Store it as a binary entry and the game exits with ERR_SYS_INVALIDRESOURCE_5.
YTD: pgDictionary<grcTexture>
The system segment of a .ytd starts with the dictionary struct; the graphics segment holds nothing but compressed pixel data.
pgDictionary<grcTexture> (at system segment offset 0)
0x20 u64 name-hash list pointer (u32 JOAAT hashes, sorted)
0x28 u16 name-hash count
0x30 u64 texture pointer list (count × u64 → grcTexture)
0x38 u16 texture count
grcTexture (64-bit layout)
0x00 u64 VFT
0x28 u64 NamePointer → NUL-terminated char*
0x50 u16 Width
0x52 u16 Height
0x58 u32 Format FourCC: "DXT1", "DXT5", "ATI1", "ATI2", or a D3D format code
0x5D u8 Levels (mip count)
0x70 u64 DataPointer → graphics segment, mip 0
DataPointer is at 0x70, not 0x60. 0x60 is a plausible guess that decodes to garbage on real files. If your texture rips come out as noise, this is the first offset to check.
FourCC → block format, and how much data mip 0 occupies:
| Format | Decodes as | Mip-0 size |
|---|---|---|
DXT1 | BC1 (RGB + 1-bit alpha) | ceil(w/4)·ceil(h/4)·8 |
DXT3 | BC2 | ceil(w/4)·ceil(h/4)·16 |
DXT5 | BC3 (the clothing default) | ceil(w/4)·ceil(h/4)·16 |
ATI1 | BC4 (single channel) | ceil(w/4)·ceil(h/4)·8 |
ATI2 | BC5 (two channel, normal maps) | ceil(w/4)·ceil(h/4)·16 |
A8R8G8B8 / A8B8G8R8 | uncompressed BGRA / RGBA | w·h·4 |
A8 / L8 | alpha / luminance | w·h |
Mips follow mip 0 contiguously, each level ¼ the pixel count (block formats never shrink below one 4×4 block per dimension).
YDD/YDR: the drawable walk
A .ydr is a single Drawable at the resource root; a .ydd is a pgDictionary<Drawable> with the same list shape as the YTD dictionary (ptr @ 0x30, count @ 0x38). From a Drawable down to vertices:
Drawable
+0x10 ShaderGroup pointer
+0x50 DrawableModels High → { u64 ptr → [DrawableModel...], u16 count @ +0x08 }
+0x58 DrawableModels Med (same shape)
+0x60 DrawableModels Low
+0x68 DrawableModels VLow
DrawableModel
+0x08 geometries list pointer
+0x10 geometry count
Geometry
+0x18 VertexBuffer pointer
+0x38 IndexBuffer pointer
+0x58 IndicesCount (u32)
+0x60 VerticesCount (u16)
VertexBuffer IndexBuffer
+0x08 stride (u16) +0x08 count (u32)
+0x10 data pointer +0x10 data pointer (u16 index array,
+0x18 count (u32) triangle list)
+0x30 declaration pointer
VertexDeclaration
+0x00 channelFlags (u32)
+0x04 stride (u16)
+0x06 channel count (u8)
+0x08 16 × 4-bit component type codes
Decoding the vertex declaration
channelFlags is a 16-bit presence mask over the fixed semantic order:
| Bit | Semantic |
|---|---|
| 0 | Position |
| 1 | BlendWeights |
| 2 | BlendIndices |
| 3 | Normal |
| 4 / 5 | Colour0 / Colour1 |
| 6 | TexCoord0 (the UV set) |
| 7 | TexCoord1 |
| 14 | Tangent |
Each present channel's byte offset within a vertex is the sum of the sizes of the present channels below it. Sizes come from the per-channel 4-bit type code via:
TYPE_SIZE = [2, 4, 4, 8, 8, 8, 12, 16, 4, 4, 4, 4, 4, 2, 2, 4]
(index by type code; 1 = Half2, 5 = Float2, 6 = Float3,
7 = Float4, 9 = UByte4 are the codes seen on real clothing)
Always assert Σ(channel sizes) == declared stride. The check is cheap and it catches a mis-read table before you ship garbage vertices. Real clothing example: channelFlags 0x40ff, 9 channels, 72-byte stride, TexCoord0 at +0x28 as 2×float32. Some meshes store UVs as Half2 instead, so handle the half-float path.
The V convention
GTA stores the texture V coordinate negated: real clothing data sits in v ∈ [-0.994, -0.007]. The correct texture row for a vertex is frac(v) under wrap addressing, which is why renderers must wrap, not clamp. A clamping sampler smears edge texels across the whole mesh (see the note in §16). If your UV display comes out vertically flipped, the fix is choosing frac(v) vs frac(-v) consistently in every place that maps UV to texture rows. Flipping only one consumer moves paint and decals to the wrong place while the display still looks right.
Safe texture repack
Editing pixels inside a .ytd without corrupting it does not require re-serializing the structs. This strategy stays structurally valid by construction:
- Keep the system segment byte-identical, leaving every struct, pointer, dimension and format untouched.
- Re-encode the edited RGBA to the texture's existing compressed format at its existing dimensions.
- Overwrite mip 0 (and lower mips where the re-encode fits) at the texture's DataPointer in a copy of the graphics segment.
- Re-serialize with the original header flags. Since no size changed, every offset and page count is still correct.
Because dimensions and format never change, all offsets and sizes are identical to the source file, and the game's structural gates (RSC7 magic + version, resource-not-binary) are satisfied by construction. Textures in formats you can't re-encode should be skipped with a warning rather than written approximately.
Provenance: offsets and the walk read from CodeWalker.Core and byte-verified against real GTA V clothing files; the decode/repack path is validated by round-trip unit tests over those files, including a repacked .ytd whose system segment stays bit-identical.