• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Help reading blp file on c#

Status
Not open for further replies.

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
Currently there are no C++ (can be integrated into C#) libraries which correctly parse blp0/1. I do plan on making one eventually but unfortunately I have too many projects at the moment to consider.

I could create a simple Java (jar) program that could be run from the command line to convert BLP0/1 files into tga or png files. I would not advise conversion directly to jpg due to the losy nature of jpg, at least in its most common form.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
Thanks guys, I'm using PitzerMike's blpaletter, although it makes me a bit complicated since I have to adapt some of its functions.
That library seems wrong. For example it is not correctly parsing the alphaBits and hasMipmaps fields.

Here is an implementation for Java ImageIO framework which can also be used as a reference.
 
Level 9
Joined
Jun 21, 2012
Messages
432
@Shadow Daemon gave me a command-line version of BLPLab a few years ago. Here it is, help is in blplabcl.txt:
My antivirus will not let me use blplabcl.exe :D

113-png.275512

That library seems wrong. For example it is not correctly parsing the alphaBits and hasMipmaps fields.

Here is an implementation for Java ImageIO framework which can also be used as a reference.
With this code I am able to read the Blp header, however I do not know how to read the image data :/
PHP:
public BlpFile(Stream stream)
        {
            str = stream;
            byte[] buffer = new byte[4];

            str.Read(buffer, 0, 4);


            //Prints BLP type
            DebugMsg(Encoding.ASCII.GetString(buffer, 0, 4));


            // Reading compression
            str.Read(buffer, 0, 4);
            uint compression = BitConverter.ToUInt32(buffer, 0);

            DebugMsg(compression.ToString());
           
            // Reading encoding, alphaBitDepth, alphaEncoding and hasMipmaps
            str.Read(buffer, 0, 4);

            encoding = buffer[0];
            alphaDepth = buffer[1];
            alphaEncoding = buffer[2];
            hasMipmaps = buffer[3];

            // Reading width
            str.Read(buffer, 0, 4);
            width = BitConverter.ToInt32(buffer, 0);

            DebugMsg(width.ToString());

            // Reading height
            str.Read(buffer, 0, 4);
            height = BitConverter.ToInt32(buffer, 0);

            DebugMsg(height.ToString());
           
            // Reading MipmapOffset Array
            for (int i = 0; i < 16; i++)
            {
                str.Read(buffer, 0, 4);
                mipmapOffsets[i] = BitConverter.ToUInt32(buffer, 0);
            }

            // Reading MipmapSize Array
            for (int i = 0; i < 16; i++)
            {
                str.Read(buffer, 0, 4);
                mippmapSize[i] = BitConverter.ToUInt32(buffer, 0);
            }

            // When encoding is 1, there is no image compression and we have to read a color palette
            if (encoding == 1)
            {
                // Reading palette
                for (int i = 0; i < 256; i++)
                {
                    byte[] color = new byte[4];
                    str.Read(color, 0, 4);
                    paletteBGRA[i].blue = color[0];
                    paletteBGRA[i].green = color[1];
                    paletteBGRA[i].red = color[2];
                    paletteBGRA[i].alpha = color[3];
                }
            }
        }
 

Attachments

  • 113.png
    113.png
    10.1 KB · Views: 577

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
With this code I am able to read the Blp header, however I do not know how to read the image data :/
That seems to be the header for BLP2 (World of Warcraft) and not BLP1 (Warcraft III).

There are a ton of C++ implementations for reading BLP2. However my Java, and possibly GhostWolf's JavaScrpt are the only BLP1 libraries to correctly read the format. TriggerHappy's PHP mostly reads BLP1, but appears to be lacking decoding logic for index content with 1 and 4 bit alpha packing.
 
Level 9
Joined
Jun 21, 2012
Messages
432
Well I have "fixed" the code based on magos blp format specification, does anyone have an idea that I am doing wrong here?

CSS:
        public static void DebugMsg(string msg)
        {
            MessageBox.Show(msg, "", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }

        public string ReadString(byte[] buffer, int count)
        {
            str.Read(buffer, 0, count);
            return Encoding.ASCII.GetString(buffer, 0, 4);
        }

        public bool ReadBool(byte[] buffer, int count)
        {
            str.Read(buffer, 0, count);
            return BitConverter.ToBoolean(buffer, 0);
        }

        public uint ReadUInt32(byte[] buffer, int count)
        {
            str.Read(buffer, 0, count);
            return BitConverter.ToUInt32(buffer, 0);
        }

        public int ReadInt32(byte[] buffer, int count)
        {
            str.Read(buffer, 0, count);
            return BitConverter.ToInt32(buffer, 0);
        }

        public BlpFile(Stream stream)
        {
            str = stream;
            byte[] buffer = new byte[4];

            type = ReadString(buffer, 4);
            compression = ReadUInt32(buffer, 4);
            alphaFlags = ReadUInt32(buffer, 4);
            width = ReadUInt32(buffer, 4);
            height = ReadUInt32(buffer, 4);
            alphaEncoding = ReadUInt32(buffer, 4);
         
            //read unused flags
            hasMipmaps = ReadBool(buffer, 4);

            for (int i = 0; i < 16; i++)
                mipmapOffset[i] = ReadUInt32(buffer, 4);
            for (int i = 0; i < 16; i++)
                mipmapSize[i] = ReadUInt32(buffer, 4);

            while (mipmapOffset[mipmapCount] != 0 && mipmapSize[mipmapCount] != 0)
                mipmapCount++;

            if (0 == compression)
            {
                jpegHeaderSize = ReadUInt32(buffer, 4);

                jpegHeader = new byte[jpegHeaderSize];
                str.Read(jpegHeader, 0, Convert.ToInt32(jpegHeaderSize));

                if (type == "BLP1")
                {
                    imageData = new byte[mipmapSize[0]];
                    str.Position = mipmapOffset[0];
                    str.Read(imageData, 0, imageData.Length);


                    //My problem begins here for blp compressed:
                    int w = Convert.ToInt32(width);
                    int h = Convert.ToInt32(height);

                    bitmap = new Bitmap(w, h);
                    BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
                    Marshal.Copy(imageData, 0, bmpdata.Scan0, imageData.Length);
                    bitmap.UnlockBits(bmpdata);
                }
            }
            else if (1 == compression)
            {
                // Reading palette

                for (int i = 0; i < 256; i++)
                {
                    Palette[i].blue = ReadInt32(buffer, 4);
                    Palette[i].green = ReadInt32(buffer, 4);
                    Palette[i].red = ReadInt32(buffer, 4);
                    Palette[i].alpha = ReadInt32(buffer, 4);
                }

                int[] indexList = new int[width * height];
                int[] alphaList = new int[width * height];

                for (int i = 0; i < width * height; i++)
                    indexList[i] = ReadInt32(buffer, 4);

                if (alphaFlags == 8)
                {
                    for (int i = 0; i < width * height; i++)
                        alphaList[i] = ReadInt32(buffer, 4);
                }

                // Whats Next?

            }
 
JPEG-BLP's are basically a JPEG image (technically multiple, with mipmaps) with a BLP header. For those you can just read the first mipmap and merge it with the JPEG header. The only issue is that the colorspace may be BGR (you want RGB), so you will have to figure out how to convert it from there. PHP-BLP uses a tool called imagemagick, and I think there is a windows binary you can use, but there may be a better way with a native C# library.

For palleted BLP's you need a way to dynamically create an image and fill it up with the correct colors.

You may want to use DSG's BLP spec:

BLP Specifications (WC3)
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
does anyone have an idea that I am doing wrong here?
Mipmap count is based on the hasMipmaps. If not 0 then a full set of mipmaps (ending at 1*1) is present, otherwise only the full scale image is present.

JPEG content BLPs are literally JPEG image files. This means they have their own height and width. One must use the mipmap height and width to resize the decoded JPEG image appropriately.

Indexed colour BLP files have a header consisting of 256 BGR colour entries, each entry being 4 bytes with 1 of these being unused (no alpha).
 
Last edited:
Status
Not open for further replies.
Top