Talk:BLP

From wowdev
Revision as of 22:34, 6 April 2016 by Deamon (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
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)