Blizzard's BLP image format

Started by
14 comments, last by Vexorian 18 years, 6 months ago
We use this function to load out BLP textures in a project I'm involved with.
This is used to load BLP textures from World of Warcraft, but I'm pretty sure that they didn't modify their BLP format from Warcraft3 so this should work with it aswell.

void TextureManager::LoadBLP(GLuint id, Texture *tex){	// load BLP texture	glBindTexture(GL_TEXTURE_2D, id);	int offsets[16],sizes[16],w,h;	GLint format;	char attr[4];	MPQFile f(tex->name.c_str());	if (f.isEof()) {		tex->id = 0;		return;	}	f.seek(8);	f.read(attr,4);	f.read(&w,4);	f.read(&h,4);	f.read(offsets,4*16);	f.read(sizes,4*16);	tex->w = w;	tex->h = h;	bool hasmipmaps = attr[4]>0;	int mipmax = hasmipmaps ? 16 : 1;	if (attr[0] == 2) {		// compressed		unsigned char *ucbuf;		if (!supportCompression) ucbuf = new unsigned char[w*h*4];			format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;		int blocksize = 8;		// guesswork here :(		if (attr[1]==8) {			format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;			blocksize = 16;		} else {			if (!attr[3]) format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;		}		tex->compressed = true;		unsigned char *buf = new unsigned char[sizes[0]];		// do every mipmap level		for (int i=0; i<mipmax; i++) {			if (w==0) w = 1;			if (h==0) h = 1;			if (offsets && sizes) {				f.seek(offsets);				f.read(buf,sizes);				int size = ((w+3)/4) * ((h+3)/4) * blocksize;				if (supportCompression) {					glCompressedTexImage2DARB(GL_TEXTURE_2D, i, format, w, h, 0, size, buf);				} else {					decompressDXTC(format, w, h, size, buf, ucbuf);					glTexImage2D(GL_TEXTURE_2D, i, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ucbuf);				}			} else break;			w >>= 1;			h >>= 1;		}		delete[] buf;		if (!supportCompression) delete[] ucbuf;	}	else if (attr[0]==1) {		// uncompressed		unsigned int pal[256];		f.read(pal,1024);		unsigned char *buf = new unsigned char[sizes[0]];		unsigned int *buf2 = new unsigned int[w*h];		unsigned int *p;		unsigned char *c, *a;		int alphabits = attr[1];		bool hasalpha = alphabits!=0;		tex->compressed = false;		for (int i=0; i<mipmax; i++) {			if (w==0) w = 1;			if (h==0) h = 1;			if (offsets && sizes) {				f.seek(offsets);				f.read(buf,sizes);				int cnt = 0;				p = buf2;				c = buf;				a = buf + w*h;				for (int y=0; y<h; y++) {					for (int x=0; x<w; x++) {						unsigned int k = pal[*c++];						k = ((k&0x00FF0000)>>16) | ((k&0x0000FF00)) | ((k& 0x000000FF)<<16);						int alpha;						if (hasalpha) {							if (alphabits == 8) {								alpha = (*a++);							} else if (alphabits == 1) {								//alpha = (*a & (128 >> cnt++)) ? 0xff : 0;								alpha = (*a & (1 << cnt++)) ? 0xff : 0;								if (cnt == 8) {									cnt = 0;									a++;								}							}						} else alpha = 0xff;						k |= alpha << 24;						*p++ = k;					}				}				glTexImage2D(GL_TEXTURE_2D, i, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf2);			} else break;			w >>= 1;			h >>= 1;		}		delete[] buf2;		delete[] buf;	}	f.close();	if (hasmipmaps) {		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);	} else {		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);	}	// some models require texture wrapping though	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);}


Naturally you would have to change things like "MPQFile f(tex->name.c_str());" and actually open the BLP file instead, but this should give you the gist of it.

Also the BLP format is closer to TGA's, not JPEG's.
Advertisement
BLP can store both JPEG's and a palettizized version of the image. They started using palettes in War3 - FT and WoW mainly because the MPQ compression was good enough plus it didn't get lossy.

However, I already have functional loaders for both types. As mentioned in the first post it's the saver I'm interested in...
----------------------------------------MagosX.com
Never mind this thread now, it's solved. It turned out that I was saving one mipmap less than neccessary (and Warcraft 3 wasn't smart enough to fix it or tell me :) ).
----------------------------------------MagosX.com
By the way Magos, how do you load the first JPG mip map from a BLP? Cause I tried that based on the specifications but I get a JPG with wrong colors that is only openable by windows XP and not ijl
------ XYE - A new edition of the classic Kye
I just appended the header with the first mipmap (of size Size[0] at offset Offset[0]) and got a working JPEG. Note that it has 4 channels (RGBA), not 3. If the colors are reversed it may be stored as BGR rather than RGB, can't remember right now. If this is the case just swap the R and B components after loading.

I put up my own interpretation of the BLP (and MDX) format here, it may (or may not) be of help:
http://Magos.TheJeffFiles.com/War3ModelEditor/
----------------------------------------MagosX.com
Wow that seems to be one of the best tools ever made for war3 although the war3 community has been dying latelly. Thanks, it would of course be of help mostly because it is the first time in 3 months I finally find help about the subject
------ XYE - A new edition of the classic Kye

This topic is closed to new replies.

Advertisement