(原创)bls文件格式的解析

wow的shader都是放在bls文件中的,前几天对这些文件分析了一下,发现wowdev.org里对bls的解析不大对,这里对bls的文件格式做一下说明。

文件前四个字节肯定是"SVXG"或"SPXG",分别表示这个bls里保存的是vs或ps。 
接下来一个uint表示版本号,没什么用。
接下来一个uint是用来表示每个profile里的shader数目,以下检测pc。
接下来是data block的偏移量,如果是vs,会有6个偏移量,如果是ps,会有12个偏移量,其中有些可能会是0。
然后就可以根据这些偏移量,移动到文件中的指定位置,开始读取shader的数据和代码了。

以下是我写的一个解析bls文件的代码,可以把指定的bls里的shader输出到一个文本文件中。

/********************************************************************
filename:   Blsparser.cpp
created:    2006/07/29
author:     Weiliang Xie (feiyurainy at 163.com)

purpose:    To parse the wow shader file(.bls),and log the shader data
            a file.
*********************************************************************/

#include <iostream>
#include <fstream>
#include <string>

#include <assert.h>

#include <d3dx9.h>
#include <dxerr9.h>

typedef unsigned int uint32;

struct BlsHeader
{
    char    shaderType[4];  /// shader type,must be "SPXG" or "SVXG"
    uint32  version;        /// shader version,useless
    uint32  shaderCount;    /// shader count in each shader block
};

/// global shader header for the input bls file
BlsHeader gBlsHeader;

/// input bls file stream
std::ifstream gBlsFStream;

/// output log file stream
std::ofstream gOutputFile;

/** read the data from the gBlsFStream
@param count num of byte to read
@return num of byte read
*/
size_t _readData(void* buf, size_t count)
{
    gBlsFStream.read( static_cast<char*>(buf), static_cast<std::streamsize>(count) );

    return gBlsFStream.gcount();
}

/** parse the bls file,and log the shader data to file
@param offsetCount the num of block offset, in vertex shader file, it must
       be 6, and in pixel shader file, it must be 12
*/
void _parseShaderData(const size_t offsetCount)
{
    uint32* blockOffset = new uint32[offsetCount];

    // read the offsets of blocks
    _readData(blockOffset, sizeof(uint32) * offsetCount);

    // for each block
    for (size_t offsetIndex = 0; offsetIndex < offsetCount; ++offsetIndex)
    {
        uint32 offset = blockOffset[offsetIndex];

        // some block offset is zero, i don't know why
        if (offset > 0)
        {
            // go to the address of the block
            gBlsFStream.seekg(offset, std::ios::beg);

            // for each shader in this block
            for (uint32 shaderIndex = 0; shaderIndex < gBlsHeader.shaderCount; ++shaderIndex)
            {
                gOutputFile << "===== Shader start : " << std::endl;

                /** block header is four uint
                0x0 number of shader parameters
                if this number is not zero, there will be some string follow it,
                each string contain a parameter name and value, and the size is 0x90
                0x4 number of shader texture names
                if this number is not zero, there will be some string follow it,
                each string contain a texture name, and the size is 0x90
                0x8 always 0 or 2
                useless
                0xc number byte of shader code text
                the size of code text
                */
                uint32 blockHeader[4] = {0};

                _readData(&blockHeader[0], sizeof(uint32));

                if (blockHeader[0] > 0)
                {
                    gOutputFile << "Parameter section start :" << std::endl;

                    // log each parameter name
                    for (uint32 i = 0; i < blockHeader[0]; ++i)
                    {
                        char paraName[0x90];
                        _readData(paraName, 0x90);

                        gOutputFile << paraName << std::endl;
                    }

                    gOutputFile << "Parameter section end/n" << std::endl;
                }

                _readData(&blockHeader[1], sizeof(uint32));

                if (blockHeader[1] > 0)
                {
                    gOutputFile << "Texture names section start :/n" << std::endl;

                    // log each texture name
                    for (uint32 i = 0; i < blockHeader[1]; ++i)
                    {
                        char texName[0x90];
                        _readData(texName, 0x90);

                        gOutputFile << texName << std::endl;
                    }

                    gOutputFile << "Texture names section end/n" << std::endl;
                }

                _readData(&blockHeader[2], sizeof(uint32));

                // get the size of shader code text
                _readData(&blockHeader[3], sizeof(uint32));

                uint32 codeSize = blockHeader[3];
                if (codeSize > 0)
                {
                    gOutputFile << "Code section start :" << std::endl;

                    char* codeText = new char[codeSize];

                    // read code text
                    size_t read = _readData(codeText, codeSize);

                    assert (read == codeSize);

                    size_t pos = gBlsFStream.tellg();
                    // if the first char is '!', this shader is written by
                    // arbfp or arbvp, and there will be some unused datas
                    // between two shaders
                    if (codeText[0] == '!')
                    {
                        gOutputFile << codeText << std::endl;

                        // find out the length of the unused datas
                        size_t nowPos = gBlsFStream.tellg();

                        size_t skipCount = 0;

                        if (nowPos % 4 == 0)
                            skipCount = 0;
                        else
                            skipCount = 4 - nowPos % 4;

                        // and skip to the next shader
                        gBlsFStream.seekg(skipCount, std::ios::cur);
                    }
                    else // it is the dx shader
                    {
                        // use d3dx to get the shader code from the binary shader code
                        LPD3DXBUFFER codeBuffer;
                        HRESULT hr = D3DXDisassembleShader(
                            static_cast<DWORD*>( static_cast<void*>(codeText) ),
                            0, NULL, &codeBuffer );

                        if (FAILED(hr))
                        {
                            std::cout << DXGetErrorDescription9(hr) << std::endl;
                        }

                        gOutputFile << static_cast<char*>( codeBuffer->GetBufferPointer() ) << std::endl;
                    }

                    gOutputFile << "Code section end/n" << std::endl;

                    delete [] codeText;
                }

                gOutputFile << "===== Shader end/n" << std::endl;
            } // end of each shader
        }
    } // end of each block

    delete [] blockOffset;
}

int main(int argc, char **argv)
{
    gBlsFStream.open(argv[1], std::ios::binary | std::ios::in);

    if (!gBlsFStream)
    {
        std::cout << "can't find the file : " << argv[1] << std::endl;
        return 1;
    }

    std::string outputName(argv[1]);
    outputName += ".log";

    gOutputFile.open(outputName.c_str(), std::ios::out | std::ios::binary);

    if (!gOutputFile)
    {
        std::cout << "can't create the output file : " << outputName << std::endl;
        return 1;
    }

    // read the header
    _readData(&gBlsHeader, sizeof(BlsHeader));

    gOutputFile << "shader type : " << gBlsHeader.shaderType << std::endl;

    size_t offsetCount;

    // find out this bls file is vs or ps
    if (gBlsHeader.shaderType[1] == 'V')
    {
        offsetCount = 6;       
    }
    else if (gBlsHeader.shaderType[1] == 'P')
    {
        offsetCount = 12;
    }
    else
    {
        std::cout << "the content of the file is wrong : " << argv[1] << std::endl;
        return 1;
    }

    _parseShaderData(offsetCount);

    gBlsFStream.close();
    gOutputFile.close();

    return 0;
}


 

你可能感兴趣的:(ios,String,File,header,each,shader)