Talk:BLP

From wowdev
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

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)