lowmiptextures - themeldingwars/Documentation GitHub Wiki
lowmiptextures.pak lives in system/vt next to the virtual texture files. It is the
low mip fallback pack: the smallest mip levels of every texture in the virtual texture,
bundled into one archive so the renderer always has something to draw while the real tiles are
still being streamed in. The client side of this is lowmiptexturecache.cpp, and
fx.lowMipTextureThreshold is the cvar that decides when it gets used.
Unlike vtex it is a completely ordinary archive: an LZMA compressed index followed by one
LZMA compressed block per texture. Everything needed to unpack it is in the file itself, no
.vtex_idx involved.
010 Editor Templates
- lowmiptextures.bt - the container
- lowmiptextures.index.bt - run this on the decompressed index
Implementations
- Forgery unpacks it, see the
vtex test-mipscommand in forgery.js
Format
Little endian.
Container
| Field | Type | Notes |
|---|---|---|
magic |
char[4] |
LMTD |
indexOriginalSize |
uint32 |
Decompressed size of the index |
indexCompressedSize |
uint32 |
Compressed size of the index |
index |
byte[indexCompressedSize] |
LZMA |
blocks |
byte[] |
The texture data, to the end of the file |
blocks starts at offset 12 + indexCompressedSize. Call that the base offset, every
compressedOffset in the index is relative to it.
Index
Once decompressed:
| Field | Type |
|---|---|
rowCount |
uint32 |
rows |
Row[rowCount] |
Row
| Field | Type | Notes |
|---|---|---|
path |
cstring |
Asset path of the texture |
unknown |
uint32 |
|
width |
uint32 |
|
height |
uint32 |
|
unknown |
uint32 |
|
pixelCount |
uint32 |
|
unknown |
uint32[3] |
|
imageFormat |
char[20] |
Null terminated and zero padded, e.g. a DXT format name |
unknown |
uint32[6] |
|
decompressedSize |
uint32 |
Size of this texture's data once decompressed |
compressedOffset |
uint32 |
Offset of the block, relative to the base offset |
compressedSize |
uint32 |
Size of the block |
Rows are variable length because of the leading path string, so the index has to be walked from the start.
Blocks
Each block is a standalone LZMA stream: slice compressedSize bytes at
baseOffset + compressedOffset and decompress to exactly decompressedSize bytes. The rows
appear in offset order in practice, but nothing depends on that.
Open questions
- Nine of the
uint32fields in a row are unnamed.width,heightandpixelCountare confirmed by their values being consistent with each other, the rest are not. Comparing a row against the corresponding entry in vtex_idx for the same asset would probably identify several of them at once. - The exact width of the
imageFormatfield. It reads as a short null terminated string followed by padding that always sums to 20 bytes, but whether the client treats that as one fixed 20 byte field or as a string plus separate fields has not been checked.