Talk:BLP
Jump to navigation
Jump to search
What's this stand for, "BLizzard Png" maybe?
BLizzard image maP? --Schlumpf
Note that this documentation is mostly wrong and actual type detection and decompression is done differently. As I failed to properly reverse it, here are some notes on the progress.
using CImVector = uint32_t; enum { MIPMAP_COUNT = 16, PALETTE_SIZE = 256, }; enum image_format_type : uint8_t { IMAGE_FORMAT_JPEG = 0, // not enabled in current clients IMAGE_FORMAT_UNK1 = 1, IMAGE_FORMAT_DXTC = 2, IMAGE_FORMAT_UNCOMPRESSED_NO_PALETTE = 3, IMAGE_FORMAT_UNCOMPRESSED_NO_PALETTE_2 = 4, // same decompression, likely other PIXEL_FORMAT }; enum PIXEL_FORMAT : uint8_t { PIXEL_FORMAT_0 = 0, PIXEL_FORMAT_1 = 1, PIXEL_FORMAT_2 = 2, PIXEL_FORMAT_PalARGB1555DitherFloydSteinberg = 3, PIXEL_FORMAT_PalARGB4444DitherFloydSteinberg = 4, PIXEL_FORMAT_PalARGB565DitherFloydSteinberg = 5, PIXEL_FORMAT_6 = 6, PIXEL_FORMAT_7 = 7, PIXEL_FORMAT_8 = 8, PIXEL_FORMAT_PalARGB2565DitherFloydSteinberg = 9, }; enum PIXEL_FORMAT_MASKS // 1 << PIXEL_FORMAT { UNCOMPRESSED_DXTC = (1 << PIXEL_FORMAT_0) | (1 << PIXEL_FORMAT_1) | (1 << PIXEL_FORMAT_7) VALID_DXTC_FORMATS = (1 << PIXEL_FORMAT_2) | (1 << PIXEL_FORMAT_PalARGB1555DitherFloydSteinberg) | (1 << PIXEL_FORMAT_PalARGB4444DitherFloydSteinberg) | (1 << PIXEL_FORMAT_PalARGB565DitherFloydSteinberg) COMPONENT_TEXTURE = (1 << PIXEL_FORMAT_PalARGB2565DitherFloydSteinberg) }; enum flag_type : uint8_t { flags_mipmap_mask = 0xF, flags_unk_0x10 = 0x10, }; struct BLPHeader { uint32_t token; // 'BLP2' uint32_t version; // always 1 image_format_type image_format; uint8_t alphaChannelBitDepth; PIXEL_FORMAT pixel_format; flag_type flags; uint32_t width; uint32_t height; uint32_t mipmapOffsets[MIPMAP_COUNT]; uint32_t mipmapSizes[MIPMAP_COUNT]; CImVector palette[PALETTE_SIZE]; }; bool has_mipmaps (BLPHeader& header) { return header.flags & flags_mipmap_mask; } bool has_unknown (BLPHeader& header) { return header.flags & flags_unk_0x10; } unsigned int mipmapLevelCount (BLPHeader& header) { return has_mipmaps (header) ? CalcLevelCount (header.width, header.height) : 1; } unsigned int CalcLevelCount (unsigned int width, unsigned int height) { if (width == 6 * height) { width /= 6; } unsigned int levelCount; for (levelCount = 1; (width > 1) && (height > 1); ++levelCount) { width >>= 1; height >>= 1; } return levelCount; } //! \note equivalent to CBLPFile::Lock() void load ( unsigned char* raw_data, BLPHeader& header , PIXEL_FORMAT dest_pixel_format , unsigned int mipmapLevel , unsigned char** dest, uint* dest_size, uint* bytes_per_pixel_row ) { FAIL_IF (mipmapLevel < mipmapLevelCount (header) && header.mipmapSizes[mipmapLevel] != 0); unsigned char* source (raw_data + header.mipmapOffsets[mipmapLevel]); unsigned int const width (std::max (header.width >> mipmapLevel, 1U)); unsigned int const height (std::max (header.height >> mipmapLevel, 1U)); switch (header.image_format) { case IMAGE_FORMAT_JPEG: FAIL ("JPEG decompression not enabled"); case IMAGE_FORMAT_UNK1: *dest_size = 0; switch (dest_pixel_format) { case PIXEL_FORMAT_0: case PIXEL_FORMAT_1: FAIL ("UNSUPPORTED"); case PIXEL_FORMAT_2: *bytes_per_pixel_row = 4 * width; break; case PIXEL_FORMAT_PalARGB1555DitherFloydSteinberg: case PIXEL_FORMAT_PalARGB4444DitherFloydSteinberg: case PIXEL_FORMAT_PalARGB565DitherFloydSteinberg: *bytes_per_pixel_row = 2 * width; break; case PIXEL_FORMAT_6: case PIXEL_FORMAT_7: case PIXEL_FORMAT_8: FAIL ("UNSUPPORTED"); case PIXEL_FORMAT_PalARGB2565DitherFloydSteinberg: *dest_size += std::max (1U, width * height / 4U); *bytes_per_pixel_row = 2 * width; break; } *dest_size += *bytes_per_pixel_row * height; *dest = new unsigned char[*dest_size]; switch (dest_pixel_format) { case PIXEL_FORMAT_2: ////// TODO: MANUAL DECOMPRESS break; case PIXEL_FORMAT_PalARGB1555DitherFloydSteinberg: blpFile->DecompPalARGB1555DitherFloydSteinberg (*dest, source, width, height); break; case PIXEL_FORMAT_PalARGB4444DitherFloydSteinberg: blpFile->DecompPalARGB4444DitherFloydSteinberg (*dest, source, width, height); break; case PIXEL_FORMAT_PalARGB565DitherFloydSteinberg: blpFile->DecompPalARGB565DitherFloydSteinberg (*dest, source, width, height); break; case PIXEL_FORMAT_PalARGB2565DitherFloydSteinberg: blpFile->DecompPalARGB2565DitherFloydSteinberg (*dest, source, width, height); break; } break; case IMAGE_FORMAT_DXTC: ///// TODO: DXTC case IMAGE_FORMAT_UNCOMPRESSED_NO_PALETTE: case IMAGE_FORMAT_UNCOMPRESSED_NO_PALETTE_2: //! \note pixel formats ignored. assumed to be correct. *bytes_per_row = header.mipmapSizes[mipmapLevel] / height; *dest_size = header.mipmapSizes[mipmapLevel]; *dest = new unsigned char[header.mipmapSizes[mipmapLevel]]; std::copy (source, source + header.mipmapSizes[mipmapLevel], *dest); break; } } void Blit_Argb8888_Argb8888 (const C2iVector *dimensions, const void *src_, uint source_stride, void *dst_, uint dst_bytes_per_row) { char* dst (dst_); const char* src (src_); const uint expected_stride (4 * dimensions->x); if ( source_stride != expected_stride || source_stride != dst_bytes_per_row ) { for (int row (0); row < dimensions->y; ++row) { memcpy (dst + row * dst_bytes_per_row, src + row * source_stride, expected_stride); } } else { memcpy (dst, src, 4 * dimensions->y * dimensions->x); } } s_blits.by_src[1].by_dst[1].by_alpha[Alpha_A0] = Blit_Argb8888_Argb8888; s_blits.by_src[Argb8888].by_dst[1].by_alpha[Alpha_A0] = Blit_Argb8888_Abgr8888; s_blits.by_src[Argb8888].by_dst[Argb8888].by_alpha[Alpha_A0] = Blit_Argb8888_Argb8888; s_blits.by_src[Argb8888].by_dst[Argb8888].by_alpha[Alpha_A1] = Blit_Argb8888_Argb8888_A1; s_blits.by_src[Argb8888].by_dst[Argb8888].by_alpha[Alpha_A8] = Blit_Argb8888_Argb8888_A8; s_blits.by_src[Argb8888].by_dst[Argb4444].by_alpha[Alpha_A0] = Blit_Argb8888_Argb4444; s_blits.by_src[Argb8888].by_dst[Argb1555].by_alpha[Alpha_A0] = Blit_Argb8888_Argb1555; s_blits.by_src[Argb8888].by_dst[Rgb565].by_alpha[Alpha_A0] = Blit_Argb8888_Rgb565; s_blits.by_src[Rgb565].by_dst[Rgb565].by_alpha[Alpha_A0] = Blit_uint16_uint16; s_blits.by_src[Argb4444].by_dst[1].by_alpha[Alpha_A0] = Blit_Argb4444_Abgr8888; s_blits.by_src[Argb4444].by_dst[Argb4444].by_alpha[Alpha_A0] = Blit_uint16_uint16; s_blits.by_src[Argb1555].by_dst[Argb1555].by_alpha[Alpha_A0] = Blit_uint16_uint16; s_blits.by_src[Dxt1].by_dst[Dxt1].by_alpha[Alpha_A0] = Blit_Dxt1_Dxt1; s_blits.by_src[Dxt3].by_dst[Dxt3].by_alpha[Alpha_A0] = Blit_Dxt35_Dxt35; s_blits.by_src[Dxt5].by_dst[Dxt5].by_alpha[Alpha_A0] = Blit_Dxt35_Dxt35; s_blits.by_src[Dxt1].by_dst[Rgb565].by_alpha[Alpha_A0] = Blit_Dxt1_Rgb565; s_blits.by_src[Dxt1].by_dst[Argb1555].by_alpha[Alpha_A0] = Blit_Dxt1_Argb1555; s_blits.by_src[Dxt1].by_dst[Argb8888].by_alpha[Alpha_A0] = Blit_Dxt1_Argb8888; s_blits.by_src[Dxt3].by_dst[Argb4444].by_alpha[Alpha_A0] = Blit_Dxt3_Argb4444; s_blits.by_src[Dxt3].by_dst[Argb8888].by_alpha[Alpha_A0] = Blit_Dxt3_Argb8888; s_blits.by_src[Dxt5].by_dst[Argb4444].by_alpha[Alpha_A0] = Blit_Dxt5_Argb4444; s_blits.by_src[Dxt5].by_dst[Argb8888].by_alpha[Alpha_A0] = Blit_Dxt5_Argb8888; s_blits.by_src[6].by_dst[6].by_alpha[Alpha_A0] = Blit_uint16_uint16; s_blits.by_src[10].by_dst[10].by_alpha[Alpha_A0] = Blit_Argb8888_Argb8888; s_blits.by_src[11].by_dst[11].by_alpha[Alpha_A0] = Blit_uint64_uint64; s_blits.by_src[12].by_dst[12].by_alpha[Alpha_A0] = Blit_Argb8888_Argb8888; s_blits.by_src[13].by_dst[13].by_alpha[Alpha_A0] = Blit_uint128_uint128; s_blits.by_src[14].by_dst[14].by_alpha[Alpha_A0] = Blit_Argb8888_Argb8888; switch ( blpFile->header.image_format ) { case 0: SErrPrepareAppFatal("/Users/patchman/buildserver/wow-b/work/WoW-code/trunk/WoW/Source/Mac/../../../Engine/Source/BLPFile/blp.cpp", 772); SErrDisplayAppFatal("%s: JPEG decompression not enabled", filename); return result; case 1: v15 = blpFile->header.width >> mipMapLevel; if ( !v15 ) v15 = 1; if ( pixel_format <= PIXEL_FORMAT_PalARGB565DitherFloydSteinberg ) { if ( pixel_format >= PIXEL_FORMAT_PalARGB1555DitherFloydSteinberg ) { *dest_size = 2 * v15; goto LABEL_33; } if ( pixel_format == PIXEL_FORMAT_2 ) { *dest_size = 4 * v15; goto LABEL_33; } LABEL_41: SErrDisplayError(0x85100000u, "/Users/patchman/buildserver/wow-b/work/WoW-code/trunk/WoW/Source/Mac/../../../Engine/Source/BLPFile/blp.cpp", 0x194u, "0", 0, 1, 0, 286331153); } if ( pixel_format != PIXEL_FORMAT_PalARGB2565DitherFloydSteinberg ) goto LABEL_41; *dest_size = 2 * v15; LABEL_33: result = CBLPFile::DecompPalDither(blpFile, pixel_format, mipMapLevel, dest, source); blpFile->uncompressed_data = dest; return result; case 2: if ( pixel_format > (unsigned int)PIXEL_FORMAT_LAST ) goto LABEL_53; v8 = 1 << pixel_format; if ( (1 << pixel_format) & 0x83 ) SErrDisplayError(0x85100000u, "/Users/patchman/buildserver/wow-b/work/WoW-code/trunk/WoW/Source/Mac/../../../Engine/Source/BLPFile/blp.cpp", 0x2D9u, "0", 0, 1, 0, 286331153); if ( BYTE1(v8) & 2 ) { SErrPrepareAppFatal("/Users/patchman/buildserver/wow-b/work/WoW-code/trunk/WoW/Source/Mac/../../../Engine/Source/BLPFile/blp.cpp", 750); SErrDisplayAppFatal("%s: component texture saved as DXT compressed", filename); } if ( !(v8 & 0x3C) ) { LABEL_53: SErrPrepareAppFatal("/Users/patchman/buildserver/wow-b/work/WoW-code/trunk/WoW/Source/Mac/../../../Engine/Source/BLPFile/blp.cpp", 754); SErrDisplayAppFatal("%s: tried to decompress a DXT texture into pixel format %d", filename, pixel_format); } v9 = 1; if ( blpFile->header.width >> mipMapLevel ) v9 = blpFile->header.width >> mipMapLevel; v18 = v9; v10 = 1; if ( blpFile->header.height >> mipMapLevel ) v10 = blpFile->header.height >> mipMapLevel; v19 = v10; v11 = blpFile->header.pixel_format; if ( v11 > 9 ) SErrDisplayError(0x85100000u, "/Users/patchman/buildserver/wow-b/work/WoW-code/trunk/WoW/Source/Mac/../../../Engine/Source/BLPFile/blp.cpp", 0x10Bu, "pixelFormat < NUM_PIXEL_FORMATS", 0, 1, 0, 286331153); srcFmt = GetBlitFormat_PIXEL_FORMAT_::blitFmt[v11]; if ( (signed int)pixel_format > 9 ) SErrDisplayError(0x85100000u, "/Users/patchman/buildserver/wow-b/work/WoW-code/trunk/WoW/Source/Mac/../../../Engine/Source/BLPFile/blp.cpp", 0x10Bu, "pixelFormat < NUM_PIXEL_FORMATS", 0, 1, 0, 286331153); v12 = GetBlitFormat_PIXEL_FORMAT_::blitFmt[pixel_format]; dstSize = CalcRowStride(GetBlitFormat_PIXEL_FORMAT_::blitFmt[pixel_format], v9, v10); v13 = CalcRowStride(srcFmt, v18, v19); v14 = blpFile->header.height >> mipMapLevel; if ( !v14 ) v14 = 1; dimensions.x = v18; dimensions.y = v14; return Blit(&dimensions, 0, source, v13, srcFmt, dest, dstSize, v12); case 3: case 4: memcpy(dest, source, blpFile->header.mipMapSizes[mipMapLevel]); return 1; } } class CBLPFile { void* field_0; BLPHeader* header; unsigned char* m_inMemoryImage; // raw data from file uint32_t field_49C; uint32_t mipmapLevelCount; uint32_t field_4A4; uint32_t field_4A8; void *uncompressed_data; // temporary storage of uncompressed image until Unlock() is called };
Pixel format 8 exists in WotLK. Example: world/nodxt/generic/passivedoodads/volumetriclights/lightbeama.blp There it is used with COLOR_PALETTE encoding
--Deamon (talk) 22:34, 6 April 2016 (CEST)