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

Implementations

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 uint32 fields in a row are unnamed. width, height and pixelCount are 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 imageFormat field. 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.