Textures (THP and TXP)
A texture is two PAK entries with one name: a .thp header of 84 bytes that wraps the PS3’s own texture descriptor (CellGcmTexture), and a .txp with the pixels, a chain of mip levels from the largest to the smallest.
There are 27,446 headers and 28,046 pixel entries on the disc; none is compressed in the PAK. Proven · all 336 PAKs
A header normally has its pixels in the same section. 97 headers in MAIN and CACHE_MAIN (Scaleform UI) have them in LOCAL and CACHE_LOCAL, and 600 pixel entries named <name>_m1.txp have no header (the tail of a cube map, below). Proven · all 336 PAKs
All numbers are big-endian.
Header
Section titled “Header”The CellGcmTexture descriptor starts at 0x3C and ends the header.
| Offset | Size | Type | Field | Confidence | Notes |
|---|---|---|---|---|---|
| 0x0 | 4 | u8[4] = AF AF 00 03 | magic | Proven | |
| 0x4 | 4 | u32 = 0x54 | header_size | Proven | |
| 0x8 | 4 | u32 = 0x003C0000 | gcm_offset | Proven | the 0x3C is the offset of the descriptor |
| 0xC | 4 | u32 = 0xBEEFFACE | sentinel | Proven | |
| 0x10 | 2 | u16 | kind | Unknown | 1 for 2D, 4 for every cube header, 2 for volumes |
| 0x12 | 2 | u16 | unknown | Unknown | 16 for B8; 1 or 2 for ARGB; 2 for DXT1; 4 or 8 for DXT5 |
| 0x14 | 2 | u16 | width | Proven | equals the descriptor's width in all headers |
| 0x16 | 2 | u16 | height | Proven | |
| 0x18 | 2 | u16 | pitch | Observed | bytes per row of the base level for swizzled textures; linear ones differ |
| 0x1A | 2 | u16 | mip_count_shifted | Observed | mip count << 8 when there is more than one mip |
| 0x1C | 4 | u32 | identifier | Unknown | not the PAK entry's hash (0 of 27,446 match); its last byte follows the last letter of the name |
| 0x20 | 4 | char16[2] | leaked_text | Unknown | two UTF-16 characters of name text, or 0 (8,661 headers) |
| 0x24 | 16 | u32[4] | tail_ends | Observed | cumulative byte ends of the mip levels above the prefetch tail; 0 for unused |
| 0x34 | 4 | u32 | zero | Observed | |
| 0x38 | 4 | u32 = 0x3C | gcm_offset_again | Observed | |
| 0x3C | 1 | u8 | format | Proven | |
| 0x3D | 1 | u8 | mip_count | Proven | |
| 0x3E | 1 | u8 | dimension | Proven | 2 for 2D, 3 for a volume |
| 0x3F | 1 | u8 | cubemap | Proven | 1 for a cube |
| 0x40 | 4 | u32 | remap | Proven | channel selectors, see below |
| 0x44 | 2 | u16 | width | Proven | |
| 0x46 | 2 | u16 | height | Proven | |
| 0x48 | 2 | u16 | depth | Proven | |
| 0x4A | 1 | u8 | location | Proven | 0 in every header |
| 0x4B | 1 | u8 | pad | Proven | 0 |
| 0x4C | 4 | u32 | pitch | Proven | |
| 0x50 | 4 | u32 | offset | Proven | 0 in every header |
Most of the descriptor is what the console’s graphics library defines, so its meaning is Sourced · RPCS3 and the PS3 graphics headers and its values are checked here. The remaining header words (kind, the 16-bit word after it, the identifier and the leaked text) have no known use.
Formats
Section titled “Formats”The descriptor’s format byte takes the following values; adding 0x20 marks the texture as linear (stored row by row at a pitch) instead of swizzled. No other formats, and no unnormalised (0x40) flag, occur. Proven · all 27,446 headers
| Format | Meaning | Swizzled | Linear (+0x20) |
|---|---|---|---|
0x86 |
DXT1 (BC1) | 12,674 | 310 |
0x88 |
DXT5 (BC3) | 10,007 | 339 |
0x81 |
B8, one byte per pixel (the remap makes it grey) | 2,474 | 0 |
0x85 |
A8R8G8B8 | 1,631 | 11 |
The remap is 0xAAE4 in 24,972 headers and 0xA9FF in all 2,474 B8 headers. Proven · all 27,446 headers
2D textures number 26,628 and volumes 818; 1,200 headers have the cube flag (600 cube maps, each header appearing in two sections). Proven · all 27,446 headers
Mip counts run from 1 to 12 (10 mips: 7,959 headers; 9: 7,376; 1: 4,215; 8: 3,153; 11: 2,721). Proven · all 27,446 headers
The commonest sizes are 512 × 512 (7,293), 256 × 256 (6,111), 128 × 128 (4,426) and 1024 × 1024 (2,529); UI textures are linear and may be any size (1280 × 720, 355 × 280, 27 × 27). Proven · all 27,446 headers
Pixels
Section titled “Pixels”The size of every mip level follows from the header: DXT is ceil(w/4) × ceil(h/4) × 8 bytes (DXT1) or × 16 (DXT5), at least one block; B8 is w × h; ARGB is 4 × w × h. A swizzled texture’s chain is these sizes added from the largest level down. Proven · 27,349 pixel entries; 0 mismatches
A linear texture uses the base level’s pitch for every mip level: each level occupies the pitch times its row count (ceil(h/4) rows for DXT, h for ARGB). Proven · all 657 linear pixel entries
Swizzled levels are in Morton (Z) order, with the bits of x on the even positions and y on the odd; once the shorter side is used up, the remaining bits of the longer one continue linearly. DXT blocks keep PC byte order. Observed · decoded and inspected
Decoded B8 (ambient occlusion maps), ARGB (art, in A, R, G, B byte order), DXT1 (brick), DXT5 (normal maps), and linear DXT and ARGB (1280 × 720 screens) were rendered and looked correct. Observed
Cube maps
Section titled “Cube maps”A cube map has six faces one after another in the chain; the faces of a swizzled cube are aligned to 128 bytes. Sourced · RPCS3
The padding is between faces only: each face’s mip chain is padded up to a multiple of 128 bytes before the next face, and nothing follows the sixth. A 256 × 256 DXT1 face with 9 mips is a 43,704-byte chain at a stride of 43,776, so the cube is 5 × 43,776 + 43,704 = 262,584 bytes; its tail faces are 10,936 bytes at a stride of 11,008, 65,976 bytes in all. Dividing a cube’s size by six does not give the face stride. Proven · all 600 cube maps, 3,600 faces
Each cube appears in TEXTURES_STREAMED as <name>.txp plus <name>_m1.txp (the tail), and the tail equals the prefetch copy exactly: 600 of 600 pairs, 3,600 faces, with face strides of 43,776 and 11,008 bytes. Proven · all 600 cube maps
Prefetch and streamed copies
Section titled “Prefetch and streamed copies”The TEXTURES_PREFETCH section holds a header and a pixel entry of the same name as the streamed texture. The header is the same and still states the full size, but the pixels are only the mip levels whose smaller side is at most 128 (a one-mip texture keeps its one mip). Proven · all 9,342 non-cube pairs
The prefetch pixels are byte-for-byte the last bytes of the streamed chain in all 9,342 pairs (8,934 2D textures and 408 volumes). Proven · all 9,342 non-cube pairs
The game can therefore draw with the small levels at once and stream in the large ones. Inferred
The same texture name in several PAKs always has the same bytes: of 10,725 distinct (section, name) pairs, 3,386 appear in two or more PAKs and all of those copies are identical. The 27,349 pixel entries hold 3,699,042,026 bytes; their distinct contents total 1,656,966,483 bytes, so 55.2% of the pixel bytes are duplicates. Proven · all 336 PAKs
Volume textures
Section titled “Volume textures”818 headers are volumes. All have one mip: 771 are DXT 128 × 128 × 4 (750 DXT5, 21 DXT1) and 47 are A8R8G8B8 32 × 32 × 32. Proven · all 336 PAKs The uncompressed volumes interleave the bits of x, y and z in Morton order, skipping an axis once its bits are used up. The compressed ones use the VTC arrangement, which groups the blocks of four z-planes. Sourced · RPCS3 and NV_texture_compression_vtc Both layouts were decoded; adjacent z-slices of the result are smoother than the same pixels shuffled in 797 of 818 volumes (21 tie, none is worse), and the 47 ARGB volumes read as colour lookup tables only with the axis order x, y, z. Observed The prefetch and streamed copies of the 408 volumes that have both decode to the same volume. Observed
The two 16-bit words at offset 16, the identifier at 28 and the two characters at 32 are consistent within a texture but have no use that was found. The identifier cannot be the entry hash.