Skip to content

BLP format

Blizzard's texture format (wowdev.wiki/BLP). BLP2 is version-stable — every client release from vanilla through The War Within reads the same layout — so unlike the chunked formats there is one unversioned BLP class and no for_version factory.

A file is a fixed header + 256-entry palette followed by up to 16 mipmap payloads. read()/write() move the file whole and round-trip byte-perfectly while the payloads are unmodified (unusual layouts — inter-mip gaps, trailing bytes, shared placement — are recorded and replayed). decode(level) produces an RGBA8 Image from any stored level — palettized, DXT1/3/5, BC5 and raw BGRA all decode. encode(image, settings) rebuilds the whole texture from one image: palette quantization (median cut), DXT compression and box-filtered mip generation.

import numpy as np
import wowlib
from wowlib.formats import blp

with wowlib.fs.FileSystem.open(settings) as fs:
    texture = blp.BLP()
    texture.read(fs, wowlib.FileKey("Interface/Icons/INV_Misc_QuestionMark.blp"))

    image = texture.decode()                    # level 0, RGBA8
    pixels = np.asarray(image.pixels).reshape(image.height, image.width, 4)

    pixels[..., 3] = 255                        # edit in place (zero-copy view)
    edited = blp.Image()
    edited.width, edited.height = image.width, image.height
    edited.pixels.extend(pixels.reshape(-1).tolist())
    texture.encode(edited, blp.EncodeSettings(format=blp.PixelFormat.Dxt5))
    texture.write(fs, wowlib.FileKey("Interface/Icons/INV_Misc_QuestionMark.blp"))

The BLP texture format: the version-stable BLP2 entity with byte-perfect round-trips, RGBA8 decode of every shipped encoding (palettized, DXT1/3/5, BC5, raw BGRA) and full re-encoding (palette quantization, DXT compression, mip-chain generation).

ColorEncoding

Bases: IntEnum

How a BLP's pixel payload is encoded (the header's colorEncoding byte).

Attributes:

Name Description
Jpeg

JPEG-compressed content (Warcraft III heritage; never shipped in WoW clients — wowlib preserves but cannot decode it).

Palettized

256-color palette indices, one byte per pixel, followed by a separate alpha plane of alphaDepth bits per pixel.

Dxt

DXT/S3TC block compression; the variant (BC1/BC2/BC3/BC5) follows from preferred_format and alphaDepth.

Bgra

Raw 32-bit BGRA pixels (Cataclysm+; terrain cube maps).

BgraAlt

Raw 32-bit BGRA under a different client-side PIXEL_FORMAT; identical file content to Bgra.

Jpeg class-attribute instance-attribute

Jpeg = 0

Palettized class-attribute instance-attribute

Palettized = 1

Dxt class-attribute instance-attribute

Dxt = 2

Bgra class-attribute instance-attribute

Bgra = 3

BgraAlt class-attribute instance-attribute

BgraAlt = 4

PixelFormat

Bases: IntEnum

The client-side pixel format hint (the header's preferredFormat byte). For DXT-encoded files it selects the block format: Dxt1 -> BC1, Dxt3 -> BC2, Dxt5 -> BC3, Bc5 -> BC5.

Attributes:

Name Description
Dxt1

BC1: 8-byte blocks, optional 1-bit punch-through alpha.

Dxt3

BC2: 16-byte blocks, explicit 4-bit alpha.

Argb8888

Raw 32-bit upload hint (used by Bgra-encoded files).

Argb1555

16-bit 1555 upload hint; never a file content layout.

Argb4444

16-bit 4444 upload hint; never a file content layout.

Rgb565

16-bit 565 upload hint; never a file content layout.

A8

Alpha-only upload hint; never a file content layout.

Dxt5

BC3: 16-byte blocks, interpolated 8-bit alpha.

Unspecified

No preference recorded (typical for palettized files).

Argb2565

Component-texture upload hint; never a file content layout.

Bc5

BC5: two interpolated channels (normal maps, later clients).

Dxt1 class-attribute instance-attribute

Dxt1 = 0

Dxt3 class-attribute instance-attribute

Dxt3 = 1

Argb8888 class-attribute instance-attribute

Argb8888 = 2

Argb1555 class-attribute instance-attribute

Argb1555 = 3

Argb4444 class-attribute instance-attribute

Argb4444 = 4

Rgb565 class-attribute instance-attribute

Rgb565 = 5

A8 class-attribute instance-attribute

A8 = 6

Dxt5 class-attribute instance-attribute

Dxt5 = 7

Unspecified class-attribute instance-attribute

Unspecified = 8

Argb2565 class-attribute instance-attribute

Argb2565 = 9

Bc5 class-attribute instance-attribute

Bc5 = 11

Image

Image()
Image(width: int, height: int, pixels: list[int])

A decoded texture surface: 8-bit RGBA pixels in row-major order, row 0 at the top. pixels holds width * height * 4 bytes (r, g, b, a per pixel) and maps to NumPy zero-copy; reshape to (height, width, 4).

width property writable

width: int

The width in pixels.

height property writable

height: int

The height in pixels.

pixels property

pixels: list[int]

The RGBA8 pixel bytes, width * height * 4, row-major from the top-left.

EncodeSettings

EncodeSettings()
EncodeSettings(encoding: ColorEncoding | None = ..., format: PixelFormat | None = ..., alpha_depth: int = 8, mipmaps: bool = True)

How encode() should build the file. The defaults produce what the client ships most: DXT compression with the block format chosen from the alpha depth (0 -> BC1, 1 -> BC1 punch-through, 4 -> BC2, 8 -> BC3), with a full generated mip chain.

encoding property writable

encoding: ColorEncoding

The payload encoding to produce (Palettized, Dxt or Bgra; Jpeg is not supported).

format property writable

format: PixelFormat

The DXT block format for Dxt encoding (Dxt1, Dxt3, Dxt5 or Bc5). Unspecified picks from alphaDepth: 0/1 -> Dxt1, 4 -> Dxt3, 8 -> Dxt5. Ignored for Palettized/Bgra.

alpha_depth property writable

alpha_depth: int

Alpha bits per pixel: 0, 1, 4 or 8. Selects the alpha plane depth for Palettized files and the block format for Dxt when format is Unspecified.

mipmaps property writable

mipmaps: bool

Whether to generate the full mip chain down to 1x1 (box-filtered). Off: the file holds only level 0.

BLP

BLP()

Bases: FileEntity

A BLP2 texture file — every WoW client release reads the same layout, so the class carries no client-version axis. read()/write() move the file whole with a byte-perfect round-trip while the mip payloads are unmodified; decode(level) produces an RGBA8 Image from a stored level (palettized, DXT1/3/5, BC5 and raw BGRA all decode); encode(image, settings) rebuilds the palette/compression/mip chain from one. Raw payload access goes through mip()/set_mip(). See https://wowdev.wiki/BLP.

version property writable

version: int

The header version field; 1 in every shipped file.

color_encoding property writable

color_encoding: ColorEncoding

How the pixel payload is encoded.

alpha_depth property writable

alpha_depth: int

Alpha bits per pixel (0, 1, 4 or 8): the alpha plane depth for Palettized files, and a selection hint for DXT.

preferred_format property writable

preferred_format: PixelFormat

The client-side pixel format hint; selects the DXT block format for Dxt-encoded files.

mip_flags property writable

mip_flags: int

The header's mip byte: 0 = level 0 only, 1 = generated mips, 2 = handmade mips (plus rare high flag bits, preserved verbatim).

width property writable

width: int

The level-0 width in pixels.

height property writable

height: int

The level-0 height in pixels.

palette property writable

palette: list[CImVector]

The 256-entry color table of Palettized files (b, g, r + a padding byte, preserved verbatim). Present but unused for Dxt/Bgra files.

mip_count property

mip_count: int

The number of stored mip levels (level indices 0 .. count - 1).

read

read(data: bytes) -> None
read(fs: FileSystem, key: FileKey) -> None

Parse a BLP2 file from memory, replacing this entity's contents.

Parameters:

Name Type Description Default
data bytes

the complete file bytes

required

Load the BLP from a client filesystem, replacing this entity's contents.

Parameters:

Name Description Default
fs

the filesystem gateway

required
key

the file identity (path and/or FileDataID)

required

write

write() -> bytes
write(fs: FileSystem, key: FileKey) -> None

Serialize this entity. While the mip payloads are unmodified the original file's exact layout is replayed, so an unmodified read rewrites byte-for-byte.

Returns:

Type Description
bytes

the file bytes

Serialize and store the BLP through the filesystem's project overlay.

Parameters:

Name Description Default
fs

the filesystem gateway

required
key

the file identity; must resolve to a path

required

decode

decode() -> Image
decode(level: int) -> Image

Decode mip level 0 to an RGBA8 Image.

Returns:

Type Description
Image

the decoded image

Decode one stored mip level to an RGBA8 Image.

Parameters:

Name Description Default
level

the mip level to decode

required

Returns:

Type Description
Image

the decoded image

encode

encode(image: Image) -> None
encode(image: Image, settings: EncodeSettings) -> None

Rebuild the whole texture from an RGBA8 image with the default settings (DXT, alpha depth 8 -> BC3, full mip chain).

Parameters:

Name Type Description Default
image Image

the level-0 image; width * height * 4 pixel bytes

required

Rebuild the whole texture from an RGBA8 image: sets the header fields, quantizes/compresses every level and generates the mip chain per the settings.

Parameters:

Name Type Description Default
image Image

the level-0 image; width * height * 4 pixel bytes

required
settings

encoding, block format, alpha depth and mip generation choices

required

mip

mip(level: int) -> bytes

One level's raw payload bytes (palette indices + alpha plane, DXT blocks, or BGRA pixels, per colorEncoding).

Parameters:

Name Type Description Default
level int

the mip level

required

Returns:

Type Description
bytes

a copy of the payload bytes

set_mip

set_mip(level: int, data: bytes) -> None

Replace one level's raw payload bytes verbatim. The caller owns their consistency with the header fields; changing a payload's size switches write() to the canonical contiguous layout.

Parameters:

Name Type Description Default
level int

the mip level

required
data bytes

the payload bytes

required

mip_width

mip_width(level: int) -> int

The pixel width of a mip level (level 0 halves per step, floored at 1).

Parameters:

Name Type Description Default
level int

the mip level

required

mip_height

mip_height(level: int) -> int

The pixel height of a mip level (level 0 halves per step, floored at 1).

Parameters:

Name Type Description Default
level int

the mip level

required

validate

validate() -> ValidationReport

Check the logical integrity contracts this texture must satisfy for the client to decode it — the base level's presence, the dimensions, and every stored level covering the pixels its size implies. write() never runs this; call it before writing when you want to know the result will load.

Returns:

Type Description
ValidationReport

every violated contract, in level order

ensure_valid

ensure_valid() -> None

Validate and raise on the first error instead of returning a report — the assert-style face of validate().

Returns:

Type Description
None

nothing; raises when validate() finds any error