コード例 #1
0
ファイル: GfxFragOpStencil.cs プロジェクト: yorki00/SPICA
        void ICustomSerialization.Deserialize(BinaryDeserializer Deserializer)
        {
            PICACommandReader Reader = new PICACommandReader(Commands);

            while (Reader.HasCommand)
            {
                PICACommand Cmd = Reader.GetCommand();

                uint Param = Cmd.Parameters[0];

                switch (Cmd.Register)
                {
                case PICARegister.GPUREG_STENCIL_TEST: Test = new PICAStencilTest(Param); break;

                case PICARegister.GPUREG_STENCIL_OP: Operation = new PICAStencilOperation(Param); break;
                }
            }
        }
コード例 #2
0
ファイル: GfxTextureMapper.cs プロジェクト: yorki00/SPICA
        void ICustomSerialization.Deserialize(BinaryDeserializer Deserializer)
        {
            PICACommandReader Reader = new PICACommandReader(Commands);

            int Index = 0;

            while (Reader.HasCommand && Index++ < 5)
            {
                PICACommand Cmd = Reader.GetCommand();

                uint Param = Cmd.Parameters[0];

                switch (Cmd.Register)
                {
                case PICARegister.GPUREG_TEXUNIT0_BORDER_COLOR:
                case PICARegister.GPUREG_TEXUNIT1_BORDER_COLOR:
                case PICARegister.GPUREG_TEXUNIT2_BORDER_COLOR:
                    BorderColor = new RGBA(Param);
                    break;

                case PICARegister.GPUREG_TEXUNIT0_PARAM:
                case PICARegister.GPUREG_TEXUNIT1_PARAM:
                case PICARegister.GPUREG_TEXUNIT2_PARAM:
                    MagFilter = (PICATextureFilter)((Param >> 1) & 1);
                    MinFilter = (PICATextureFilter)((Param >> 2) & 1);
                    MipFilter = (PICATextureFilter)((Param >> 24) & 1);

                    WrapV = (PICATextureWrap)((Param >> 8) & 3);
                    WrapU = (PICATextureWrap)((Param >> 12) & 3);
                    break;

                case PICARegister.GPUREG_TEXUNIT0_LOD:
                case PICARegister.GPUREG_TEXUNIT1_LOD:
                case PICARegister.GPUREG_TEXUNIT2_LOD:
                    LODBias = (((int)Param << 20) >> 20) / (float)0x100;
                    MinLOD  = (byte)((Param >> 24) & 0xf);
                    break;
                }
            }
        }
コード例 #3
0
ファイル: GfxFragOpBlend.cs プロジェクト: yorki00/SPICA
        void ICustomSerialization.Deserialize(BinaryDeserializer Deserializer)
        {
            PICACommandReader Reader = new PICACommandReader(Commands);

            while (Reader.HasCommand)
            {
                PICACommand Cmd = Reader.GetCommand();

                uint Param = Cmd.Parameters[0];

                switch (Cmd.Register)
                {
                case PICARegister.GPUREG_COLOR_OPERATION: ColorOperation = new PICAColorOperation(Param); break;

                case PICARegister.GPUREG_BLEND_FUNC: Function = new PICABlendFunction(Param); break;

                case PICARegister.GPUREG_LOGIC_OP: LogicalOperation = (PICALogicalOp)(Param & 0xf); break;

                case PICARegister.GPUREG_BLEND_COLOR: Color = new RGBA(Param); break;
                }
            }
        }
コード例 #4
0
ファイル: GFMesh.cs プロジェクト: HelloOO7/SPICA
        public GFMesh(BinaryReader Reader) : this()
        {
            GFSection MeshSection = new GFSection(Reader);

            long Position = Reader.BaseStream.Position;

            uint   NameHash = Reader.ReadUInt32();
            string NameStr  = Reader.ReadPaddedString(0x40);

            Name = NameStr;

            Reader.ReadUInt32();

            BBoxMinVector = Reader.ReadVector4(); //Is it right? seems to be 0, 0, 0, 1
            BBoxMaxVector = Reader.ReadVector4(); //Is it right? seems to be 0, 0, 0, 1

            uint SubMeshesCount = Reader.ReadUInt32();

            BoneIndicesPerVertex = Reader.ReadInt32();

            Reader.BaseStream.Seek(0x10, SeekOrigin.Current); //Padding

            List <uint[]> CmdList = new List <uint[]>();

            uint CommandsLength;
            uint CommandIndex;
            uint CommandsCount;
            uint Padding;

            do
            {
                CommandsLength = Reader.ReadUInt32();
                CommandIndex   = Reader.ReadUInt32();
                CommandsCount  = Reader.ReadUInt32();
                Padding        = Reader.ReadUInt32();

                uint[] Commands = new uint[CommandsLength >> 2];

                for (int Index = 0; Index < Commands.Length; Index++)
                {
                    Commands[Index] = Reader.ReadUInt32();
                }

                CmdList.Add(Commands);
            }while (CommandIndex < CommandsCount - 1);

            SubMeshSize[] SMSizes = new SubMeshSize[SubMeshesCount];

            //Add SubMesh with Hash, Name and Bone Indices.
            //The rest is added latter (because the data is split inside the file).
            for (int MeshIndex = 0; MeshIndex < SubMeshesCount; MeshIndex++)
            {
                GFSubMesh SM = new GFSubMesh();

                uint SMNameHash = Reader.ReadUInt32();

                SM.Name             = Reader.ReadIntLengthString();
                SM.BoneIndicesCount = Reader.ReadByte();

                for (int Bone = 0; Bone < 0x1f; Bone++)
                {
                    SM.BoneIndices[Bone] = Reader.ReadByte();
                }

                SMSizes[MeshIndex] = new SubMeshSize()
                {
                    VerticesCount  = Reader.ReadInt32(),
                    IndicesCount   = Reader.ReadInt32(),
                    VerticesLength = Reader.ReadInt32(),
                    IndicesLength  = Reader.ReadInt32()
                };

                SubMeshes.Add(SM);
            }

            for (int MeshIndex = 0; MeshIndex < SubMeshesCount; MeshIndex++)
            {
                GFSubMesh SM = SubMeshes[MeshIndex];

                uint[] EnableCommands  = CmdList[MeshIndex * 3 + 0];
                uint[] DisableCommands = CmdList[MeshIndex * 3 + 1];
                uint[] IndexCommands   = CmdList[MeshIndex * 3 + 2];

                PICACommandReader CmdReader;

                CmdReader = new PICACommandReader(EnableCommands);

                PICAVectorFloat24[] Fixed = new PICAVectorFloat24[12];

                ulong BufferFormats     = 0;
                ulong BufferAttributes  = 0;
                ulong BufferPermutation = 0;
                int   AttributesCount   = 0;
                int   AttributesTotal   = 0;

                int FixedIndex = 0;

                while (CmdReader.HasCommand)
                {
                    PICACommand Cmd = CmdReader.GetCommand();

                    uint Param = Cmd.Parameters[0];

                    switch (Cmd.Register)
                    {
                    case PICARegister.GPUREG_ATTRIBBUFFERS_FORMAT_LOW:  BufferFormats |= (ulong)Param << 0; break;

                    case PICARegister.GPUREG_ATTRIBBUFFERS_FORMAT_HIGH: BufferFormats |= (ulong)Param << 32; break;

                    case PICARegister.GPUREG_ATTRIBBUFFER0_CONFIG1:
                        BufferAttributes |= Param;
                        break;

                    case PICARegister.GPUREG_ATTRIBBUFFER0_CONFIG2:
                        BufferAttributes |= (ulong)(Param & 0xffff) << 32;
                        SM.VertexStride   = (byte)(Param >> 16);
                        AttributesCount   = (int)(Param >> 28);
                        break;

                    case PICARegister.GPUREG_FIXEDATTRIB_INDEX: FixedIndex = (int)Param; break;

                    case PICARegister.GPUREG_FIXEDATTRIB_DATA0: Fixed[FixedIndex].Word0 = Param; break;

                    case PICARegister.GPUREG_FIXEDATTRIB_DATA1: Fixed[FixedIndex].Word1 = Param; break;

                    case PICARegister.GPUREG_FIXEDATTRIB_DATA2: Fixed[FixedIndex].Word2 = Param; break;

                    case PICARegister.GPUREG_VSH_NUM_ATTR: AttributesTotal = (int)(Param + 1); break;

                    case PICARegister.GPUREG_VSH_ATTRIBUTES_PERMUTATION_LOW:  BufferPermutation |= (ulong)Param << 0; break;

                    case PICARegister.GPUREG_VSH_ATTRIBUTES_PERMUTATION_HIGH: BufferPermutation |= (ulong)Param << 32; break;
                    }
                }

                for (int Index = 0; Index < AttributesTotal; Index++)
                {
                    if (((BufferFormats >> (48 + Index)) & 1) != 0)
                    {
                        PICAAttributeName Name = (PICAAttributeName)((BufferPermutation >> Index * 4) & 0xf);

                        float Scale =
                            Name == PICAAttributeName.Color ||
                            Name == PICAAttributeName.BoneWeight ? Scales[1] : 1;

                        SM.FixedAttributes.Add(new PICAFixedAttribute()
                        {
                            Name  = Name,
                            Value = Fixed[Index] * Scale
                        });
                    }
                    else
                    {
                        int PermutationIdx = (int)((BufferAttributes >> Index * 4) & 0xf);
                        int AttributeName  = (int)((BufferPermutation >> PermutationIdx * 4) & 0xf);
                        int AttributeFmt   = (int)((BufferFormats >> PermutationIdx * 4) & 0xf);

                        PICAAttribute Attrib = new PICAAttribute()
                        {
                            Name     = (PICAAttributeName)AttributeName,
                            Format   = (PICAAttributeFormat)(AttributeFmt & 3),
                            Elements = (AttributeFmt >> 2) + 1,
                            Scale    = Scales[AttributeFmt & 3]
                        };

                        if (Attrib.Name == PICAAttributeName.BoneIndex)
                        {
                            Attrib.Scale = 1;
                        }

                        SM.Attributes.Add(Attrib);
                    }
                }

                CmdReader = new PICACommandReader(IndexCommands);

                uint BufferAddress = 0;
                uint BufferCount   = 0;

                while (CmdReader.HasCommand)
                {
                    PICACommand Cmd = CmdReader.GetCommand();

                    uint Param = Cmd.Parameters[0];

                    switch (Cmd.Register)
                    {
                    case PICARegister.GPUREG_INDEXBUFFER_CONFIG: BufferAddress = Param; break;

                    case PICARegister.GPUREG_NUMVERTICES:        BufferCount = Param; break;

                    case PICARegister.GPUREG_PRIMITIVE_CONFIG:
                        SM.PrimitiveMode = (PICAPrimitiveMode)(Param >> 8);
                        break;
                    }
                }

                SM.RawBuffer = Reader.ReadBytes(SMSizes[MeshIndex].VerticesLength);

                SM.Indices = new ushort[BufferCount];

                long IndexAddress = Reader.BaseStream.Position;

                for (int Index = 0; Index < BufferCount; Index++)
                {
                    if ((BufferAddress >> 31) != 0)
                    {
                        SM.Indices[Index] = Reader.ReadUInt16();
                    }
                    else
                    {
                        SM.Indices[Index] = Reader.ReadByte();
                    }
                }

                Reader.BaseStream.Seek(IndexAddress + SMSizes[MeshIndex].IndicesLength, SeekOrigin.Begin);
            }

            Reader.BaseStream.Seek(Position + MeshSection.Length, SeekOrigin.Begin);
        }
コード例 #5
0
        public static RenderBase.OModel loadModel(Stream data, bool keepOpen = false)
        {
            RenderBase.OModel mdl   = new RenderBase.OModel();
            BinaryReader      input = new BinaryReader(data);

            mdl.name = "model";

            long mdlStart = data.Position;

            data.Seek(0x10, SeekOrigin.Current);
            ulong mdlMagic  = input.ReadUInt64(); //gfmodel string
            uint  mdlLength = input.ReadUInt32();

            input.ReadUInt32(); //-1

            string[] effectNames   = getStrTable(input);
            string[] textureNames  = getStrTable(input);
            string[] materialNames = getStrTable(input);
            string[] meshNames     = getStrTable(input);

            input.BaseStream.Seek(0x20, SeekOrigin.Current); //2 float4 (Maybe 2 Quaternions?)

            mdl.transform     = new RenderBase.OMatrix();
            mdl.transform.M11 = input.ReadSingle();
            mdl.transform.M12 = input.ReadSingle();
            mdl.transform.M13 = input.ReadSingle();
            mdl.transform.M14 = input.ReadSingle();

            mdl.transform.M21 = input.ReadSingle();
            mdl.transform.M22 = input.ReadSingle();
            mdl.transform.M23 = input.ReadSingle();
            mdl.transform.M24 = input.ReadSingle();

            mdl.transform.M31 = input.ReadSingle();
            mdl.transform.M32 = input.ReadSingle();
            mdl.transform.M33 = input.ReadSingle();
            mdl.transform.M34 = input.ReadSingle();

            mdl.transform.M41 = input.ReadSingle();
            mdl.transform.M42 = input.ReadSingle();
            mdl.transform.M43 = input.ReadSingle();
            mdl.transform.M44 = input.ReadSingle();

            uint unkDataLen      = input.ReadUInt32();
            uint unkDataRelStart = input.ReadUInt32();

            input.ReadUInt32();
            input.ReadUInt32();

            input.BaseStream.Seek(unkDataRelStart + unkDataLen, SeekOrigin.Current); //???

            uint bonesCount = input.ReadUInt32();

            input.BaseStream.Seek(0xc, SeekOrigin.Current);

            List <string> boneNames = new List <string>();

            for (int b = 0; b < bonesCount; b++)
            {
                string boneName   = IOUtils.readStringWithLength(input, input.ReadByte());
                string parentName = IOUtils.readStringWithLength(input, input.ReadByte());
                byte   flags      = input.ReadByte();

                RenderBase.OBone bone = new RenderBase.OBone();

                bone.name     = boneName;
                bone.parentId = (short)boneNames.IndexOf(parentName);

                bone.scale = new RenderBase.OVector3(
                    input.ReadSingle(),
                    input.ReadSingle(),
                    input.ReadSingle());

                bone.rotation = new RenderBase.OVector3(
                    input.ReadSingle(),
                    input.ReadSingle(),
                    input.ReadSingle());

                bone.translation = new RenderBase.OVector3(
                    input.ReadSingle(),
                    input.ReadSingle(),
                    input.ReadSingle());

                bone.absoluteScale = new RenderBase.OVector3(bone.scale);

                mdl.skeleton.Add(bone);
                boneNames.Add(boneName);
            }

            //Materials
            List <string> matMeshBinding = new List <string>();

            input.BaseStream.Seek(mdlStart + mdlLength + 0x20, SeekOrigin.Begin);

            for (int m = 0; m < materialNames.Length; m++)
            {
                RenderBase.OMaterial mat = new RenderBase.OMaterial();

                mat.name = materialNames[m];

                ulong matMagic  = input.ReadUInt64(); //material string
                uint  matLength = input.ReadUInt32();
                input.ReadUInt32();                   //-1

                long matStart = data.Position;

                string[] unkNames = new string[4];

                for (int n = 0; n < 4; n++)
                {
                    uint maybeHash = input.ReadUInt32();
                    byte nameLen   = input.ReadByte();

                    unkNames[n] = IOUtils.readStringWithLength(input, nameLen);
                }

                matMeshBinding.Add(unkNames[0]);

                data.Seek(0xac, SeekOrigin.Current);

                long textureCoordsStart = data.Position;

                for (int unit = 0; unit < 3; unit++)
                {
                    data.Seek(textureCoordsStart + unit * 0x42, SeekOrigin.Begin);

                    uint   maybeHash = input.ReadUInt32();
                    string texName   = IOUtils.readStringWithLength(input, input.ReadByte());

                    if (texName == string.Empty)
                    {
                        break;
                    }

                    switch (unit)
                    {
                    case 0: mat.name0 = texName; break;

                    case 1: mat.name1 = texName; break;

                    case 2: mat.name2 = texName; break;
                    }

                    ushort unitIdx = input.ReadUInt16();

                    mat.textureCoordinator[unit].scaleU     = input.ReadSingle();
                    mat.textureCoordinator[unit].scaleV     = input.ReadSingle();
                    mat.textureCoordinator[unit].rotate     = input.ReadSingle();
                    mat.textureCoordinator[unit].translateU = input.ReadSingle();
                    mat.textureCoordinator[unit].translateV = input.ReadSingle();

                    uint texMapperU = input.ReadUInt32();
                    uint texMapperV = input.ReadUInt32();

                    mat.textureMapper[unit].wrapU = (RenderBase.OTextureWrap)(texMapperU & 7);
                    mat.textureMapper[unit].wrapV = (RenderBase.OTextureWrap)(texMapperV & 7);
                }

                mdl.material.Add(mat);

                input.BaseStream.Seek(matStart + matLength, SeekOrigin.Begin);
            }

            //Meshes
            for (int m = 0; m < meshNames.Length; m++)
            {
                ulong meshMagic  = input.ReadUInt64(); //mesh string
                uint  meshLength = input.ReadUInt32();
                input.ReadUInt32();                    //-1

                long meshStart = data.Position;
                //Mesh name and other stuff goes here

                input.BaseStream.Seek(0x80, SeekOrigin.Current);

                subMeshInfo info = getSubMeshInfo(input);

                for (int sm = 0; sm < info.count; sm++)
                {
                    RenderBase.OMesh obj = new RenderBase.OMesh();

                    obj.isVisible  = true;
                    obj.name       = info.names[sm];
                    obj.materialId = (ushort)matMeshBinding.IndexOf(obj.name);

                    ushort[] nodeList = info.nodeLists[sm];

                    //NOTE: All Addresses on commands are set to 0x99999999 and are probably relocated by game engine
                    PICACommandReader vtxCmdReader = info.cmdBuffers[sm * 3 + 0];
                    PICACommandReader idxCmdReader = info.cmdBuffers[sm * 3 + 2];

                    uint vshAttributesBufferStride = vtxCmdReader.getVSHAttributesBufferStride(0);
                    uint vshTotalAttributes        = vtxCmdReader.getVSHTotalAttributes(0);
                    PICACommand.vshAttribute[] vshMainAttributesBufferPermutation = vtxCmdReader.getVSHAttributesBufferPermutation();
                    uint[] vshAttributesBufferPermutation = vtxCmdReader.getVSHAttributesBufferPermutation(0);
                    PICACommand.attributeFormat[] vshAttributesBufferFormat = vtxCmdReader.getVSHAttributesBufferFormat();

                    for (int attribute = 0; attribute < vshTotalAttributes; attribute++)
                    {
                        switch (vshMainAttributesBufferPermutation[vshAttributesBufferPermutation[attribute]])
                        {
                        case PICACommand.vshAttribute.normal: obj.hasNormal = true; break;

                        case PICACommand.vshAttribute.tangent: obj.hasTangent = true; break;

                        case PICACommand.vshAttribute.color: obj.hasColor = true; break;

                        case PICACommand.vshAttribute.textureCoordinate0: obj.texUVCount = Math.Max(obj.texUVCount, 1); break;

                        case PICACommand.vshAttribute.textureCoordinate1: obj.texUVCount = Math.Max(obj.texUVCount, 2); break;

                        case PICACommand.vshAttribute.textureCoordinate2: obj.texUVCount = Math.Max(obj.texUVCount, 3); break;
                        }
                    }

                    PICACommand.indexBufferFormat idxBufferFormat = idxCmdReader.getIndexBufferFormat();
                    uint idxBufferTotalVertices = idxCmdReader.getIndexBufferTotalVertices();

                    obj.hasNode   = true;
                    obj.hasWeight = true;

                    long vtxBufferStart = data.Position;

                    input.BaseStream.Seek(info.vtxLengths[sm], SeekOrigin.Current);

                    long idxBufferStart = data.Position;

                    for (int faceIndex = 0; faceIndex < idxBufferTotalVertices; faceIndex++)
                    {
                        ushort index = 0;

                        switch (idxBufferFormat)
                        {
                        case PICACommand.indexBufferFormat.unsignedShort: index = input.ReadUInt16(); break;

                        case PICACommand.indexBufferFormat.unsignedByte: index = input.ReadByte(); break;
                        }

                        long dataPosition = data.Position;
                        long vertexOffset = vtxBufferStart + (index * vshAttributesBufferStride);
                        data.Seek(vertexOffset, SeekOrigin.Begin);

                        RenderBase.OVertex vertex = new RenderBase.OVertex();
                        vertex.diffuseColor = 0xffffffff;
                        // Fix weight problems
                        vertex.weight.Add(1);
                        vertex.weight.Add(0);
                        vertex.weight.Add(0);
                        vertex.weight.Add(0);

                        for (int attribute = 0; attribute < vshTotalAttributes; attribute++)
                        {
                            //gdkchan self note: The Attribute type flags are used for something else on Bone Weight (and bone index?)
                            PICACommand.vshAttribute    att    = vshMainAttributesBufferPermutation[vshAttributesBufferPermutation[attribute]];
                            PICACommand.attributeFormat format = vshAttributesBufferFormat[vshAttributesBufferPermutation[attribute]];
                            if (att == PICACommand.vshAttribute.boneWeight)
                            {
                                format.type = PICACommand.attributeFormatType.unsignedByte;
                            }
                            RenderBase.OVector4 vector = getVector(input, format);

                            switch (att)
                            {
                            case PICACommand.vshAttribute.position:
                                vertex.position = new RenderBase.OVector3(vector.x, vector.y, vector.z);
                                break;

                            case PICACommand.vshAttribute.normal:
                                vertex.normal = new RenderBase.OVector3(vector.x, vector.y, vector.z);
                                break;

                            case PICACommand.vshAttribute.tangent:
                                vertex.tangent = new RenderBase.OVector3(vector.x, vector.y, vector.z);
                                break;

                            case PICACommand.vshAttribute.color:
                                uint r = MeshUtils.saturate(vector.x);
                                uint g = MeshUtils.saturate(vector.y);
                                uint b = MeshUtils.saturate(vector.z);
                                uint a = MeshUtils.saturate(vector.w);
                                vertex.diffuseColor = b | (g << 8) | (r << 16) | (a << 24);
                                break;

                            case PICACommand.vshAttribute.textureCoordinate0:
                                vertex.texture0 = new RenderBase.OVector2(vector.x, vector.y);
                                break;

                            case PICACommand.vshAttribute.textureCoordinate1:
                                vertex.texture1 = new RenderBase.OVector2(vector.x, vector.y);
                                break;

                            case PICACommand.vshAttribute.textureCoordinate2:
                                vertex.texture2 = new RenderBase.OVector2(vector.x, vector.y);
                                break;

                            case PICACommand.vshAttribute.boneIndex:
                                addNode(vertex.node, nodeList, (int)vector.x);
                                if (format.attributeLength > 0)
                                {
                                    addNode(vertex.node, nodeList, (int)vector.y);
                                }
                                if (format.attributeLength > 1)
                                {
                                    addNode(vertex.node, nodeList, (int)vector.z);
                                }
                                if (format.attributeLength > 2)
                                {
                                    addNode(vertex.node, nodeList, (int)vector.w);
                                }
                                break;

                            case PICACommand.vshAttribute.boneWeight:
                                vertex.weight[0] = (vector.x / 255f);
                                if (format.attributeLength > 0)
                                {
                                    vertex.weight[1] = (vector.y / 255f);
                                }
                                if (format.attributeLength > 1)
                                {
                                    vertex.weight[2] = (vector.z / 255f);
                                }
                                if (format.attributeLength > 2)
                                {
                                    vertex.weight[3] = (vector.w / 255f);
                                }
                                break;
                            }
                        }

                        //If the node list have 4 or less bones, then there is no need to store the indices per vertex
                        //Instead, the entire list is used, since it supports up to 4 bones.
                        if (vertex.node.Count == 0 && nodeList.Length <= 4)
                        {
                            for (int n = 0; n < nodeList.Length; n++)
                            {
                                vertex.node.Add(nodeList[n]);
                            }
                            if (vertex.weight.Count == 0)
                            {
                                vertex.weight.Add(1);
                            }
                        }

                        MeshUtils.calculateBounds(mdl, vertex);
                        obj.vertices.Add(vertex);

                        data.Seek(dataPosition, SeekOrigin.Begin);
                    }

                    input.BaseStream.Seek(idxBufferStart + info.idxLengths[sm], SeekOrigin.Begin);

                    mdl.mesh.Add(obj);
                }

                input.BaseStream.Seek(meshStart + meshLength, SeekOrigin.Begin);
            }

            if (!keepOpen)
            {
                data.Close();
            }

            return(mdl);
        }
コード例 #6
0
        private bool open(string fileName)
        {
            using (FileStream data = new FileStream(fileName, FileMode.Open))
            {
                BinaryReader input = new BinaryReader(data);

                if (peek(input) == 0x00010000)
                {
                    currentFile = fileName;
                    bch         = new loadedBCH();
                    bch.isBCH   = false;
                    bch.mips.Add(new MIPlayer());
                    packPNK(data, input, bch);
                }
                if (peek(input) == 0x15041213)
                {
                    currentFile = fileName;
                    bch         = new loadedBCH();
                    bch.isBCH   = false;
                    bch.mips.Add(new MIPlayer());
                    bch.mips[0].textures.Add(loadPKM(data, input));
                }
                string magic2b = getMagic(input, 2);
                if (magic2b == "PC" || magic2b == "CM")
                {
                    bch         = new loadedBCH();
                    bch.isBCH   = false;
                    currentFile = fileName;
                    data.Seek(2, SeekOrigin.Current);
                    ushort numEntrys = input.ReadUInt16();
                    for (int i = 0; i < (int)numEntrys; i++)
                    {
                        data.Seek(4 + (i * 4), SeekOrigin.Begin);
                        uint offset = input.ReadUInt32();
                        uint end    = input.ReadUInt32();
                        uint lenth  = end - offset;
                        long rtn    = data.Position;
                        data.Seek(offset, SeekOrigin.Begin);
                        if (magic2b == "CM" & i == 0)
                        {
                            packPNK(data, input, bch);
                        }
                        if (lenth > 4)
                        {
                            if (magic2b == "PC")
                            {
                                if (peek(input) == 0x15041213)
                                {
                                    bch.mips[0].textures.Add(loadPKM(data, input));
                                }
                            }
                        }
                    }
                }
                string magic = IOUtils.readString(input, 0);
                if (magic == "BCH")
                {
                    currentFile = fileName;
                    data.Seek(4, SeekOrigin.Current);
                    byte   backwardCompatibility = input.ReadByte();
                    byte   forwardCompatibility  = input.ReadByte();
                    ushort version = input.ReadUInt16();

                    uint mainHeaderOffset      = input.ReadUInt32();
                    uint stringTableOffset     = input.ReadUInt32();
                    uint gpuCommandsOffset     = input.ReadUInt32();
                    uint dataOffset            = input.ReadUInt32();
                    uint dataExtendedOffset    = backwardCompatibility > 0x20 ? input.ReadUInt32() : 0;
                    uint relocationTableOffset = input.ReadUInt32();

                    uint mainHeaderLength      = input.ReadUInt32();
                    uint stringTableLength     = input.ReadUInt32();
                    uint gpuCommandsLength     = input.ReadUInt32();
                    uint dataLength            = input.ReadUInt32();
                    uint dataExtendedLength    = backwardCompatibility > 0x20 ? input.ReadUInt32() : 0;
                    uint relocationTableLength = input.ReadUInt32();

                    data.Seek(mainHeaderOffset, SeekOrigin.Begin);
                    uint modelsPointerTableOffset  = input.ReadUInt32() + mainHeaderOffset;
                    uint modelsPointerTableEntries = input.ReadUInt32();

                    data.Seek(mainHeaderOffset + 0x24, SeekOrigin.Begin);
                    uint texturesPointerTableOffset  = input.ReadUInt32() + mainHeaderOffset;
                    uint texturesPointerTableEntries = input.ReadUInt32();

                    bch       = new loadedBCH();
                    bch.isBCH = true;
                    for (int i = 0; i < 6; i++)
                    {
                        bch.mips.Add(new MIPlayer());
                    }
                    MipSelect.Enabled = true;

                    //Textures
                    for (int index = 0; index < texturesPointerTableEntries; index++)
                    {
                        data.Seek(texturesPointerTableOffset + (index * 4), SeekOrigin.Begin);
                        data.Seek(input.ReadUInt32() + mainHeaderOffset, SeekOrigin.Begin);

                        loadedTexture tex;
                        tex.modified             = false;
                        tex.gpuCommandsOffset    = input.ReadUInt32() + gpuCommandsOffset;
                        tex.gpuCommandsWordCount = input.ReadUInt32();
                        data.Seek(0x14, SeekOrigin.Current);
                        uint   textureNameOffset = input.ReadUInt32();
                        string textureName       = IOUtils.readString(input, textureNameOffset + stringTableOffset);

                        data.Seek(tex.gpuCommandsOffset, SeekOrigin.Begin);
                        PICACommandReader textureCommands = new PICACommandReader(data, tex.gpuCommandsWordCount);

                        tex.offset = textureCommands.getTexUnit0Address() + dataOffset;
                        RenderBase.OTextureFormat fmt = textureCommands.getTexUnit0Format();
                        Size textureSize = textureCommands.getTexUnit0Size();
                        tex.type = fmt;

                        int OGW = textureSize.Width;
                        int OGH = textureSize.Height;
                        for (int i = 0; i < 6; i++)
                        {
                            textureSize.Width  = OGW / Convert.ToInt32(Math.Pow(2, i));
                            textureSize.Height = OGH / Convert.ToInt32(Math.Pow(2, i));
                            tex.length         = returnSize(fmt, textureSize.Width, textureSize.Height);

                            if (textureSize.Height >= 8 & textureSize.Width >= 8)
                            {
                                data.Seek(tex.offset, SeekOrigin.Begin);
                                byte[] buffer = new byte[tex.length];

                                //data.Seek(tex.length + returnSize(fmt, textureSize.Width / 2, textureSize.Height / 2), SeekOrigin.Current);
                                tex.offset = (uint)data.Position;
                                input.Read(buffer, 0, tex.length);
                                Bitmap texture = TextureCodec.decode(
                                    buffer,
                                    textureSize.Width,
                                    textureSize.Height,
                                    fmt);

                                tex.texture = new RenderBase.OTexture(texture, textureName);

                                bch.mips[i].textures.Add(tex);
                                tex.offset = (uint)data.Position;
                            }
                        }
                    }

                    bch.mainHeaderOffset      = mainHeaderOffset;
                    bch.gpuCommandsOffset     = gpuCommandsOffset;
                    bch.dataOffset            = dataOffset;
                    bch.relocationTableOffset = relocationTableOffset;
                    bch.relocationTableLength = relocationTableLength;
                }
                else if (magic == "CTPK\u0001")
                {
                    currentFile = fileName;
                    data.Seek(4, SeekOrigin.Current);
                    ushort ver                  = input.ReadUInt16();
                    ushort numTexture           = input.ReadUInt16();
                    uint   TextureSectionOffset = input.ReadUInt32();
                    uint   TextureSectionSize   = input.ReadUInt32();
                    uint   HashSectionOffset    = input.ReadUInt32();
                    uint   TextureInfoSection   = input.ReadUInt32();

                    bch       = new loadedBCH();
                    bch.isBCH = false;
                    bch.mips.Add(new MIPlayer());
                    for (int i = 0; i < numTexture; i++)
                    {
                        data.Seek(0x20 * (i + 1), SeekOrigin.Begin);
                        loadedTexture tex;
                        tex.modified          = false;
                        tex.gpuCommandsOffset = (uint)(0x20 * (i + 1));
                        uint   textureNameOffset = input.ReadUInt32();
                        string textureName       = IOUtils.readString(input, textureNameOffset);
                        tex.length = input.ReadInt32();
                        tex.offset = input.ReadUInt32() + TextureSectionOffset;
                        tex.type   = (RenderBase.OTextureFormat)input.ReadUInt32();
                        ushort Width  = input.ReadUInt16();
                        ushort Height = input.ReadUInt16();
                        data.Seek(tex.offset, SeekOrigin.Begin);
                        byte[] buffer = new byte[tex.length];

                        input.Read(buffer, 0, buffer.Length);
                        Bitmap texture = TextureCodec.decode(
                            buffer,
                            Width,
                            Height,
                            tex.type);

                        tex.texture = new RenderBase.OTexture(texture, textureName);

                        tex.gpuCommandsWordCount = 0;

                        bch.mips[0].textures.Add(tex);
                    }
                }
            }

            updateTexturesList();
            return(true);
        }
コード例 #7
0
ファイル: GFShader.cs プロジェクト: yorki00/SPICA
        public GFShader(BinaryReader Reader) : this()
        {
            uint MagicNumber = Reader.ReadUInt32();
            uint ShaderCount = Reader.ReadUInt32();

            GFSection.SkipPadding(Reader.BaseStream);

            GFSection ShaderSection = new GFSection(Reader);

            Name = Reader.ReadPaddedString(0x40);

            uint Hash  = Reader.ReadUInt32();
            uint Count = Reader.ReadUInt32();

            GFSection.SkipPadding(Reader.BaseStream);

            uint CommandsLength = Reader.ReadUInt32();
            uint CommandsCount  = Reader.ReadUInt32();
            uint CommandsHash   = Reader.ReadUInt32();
            uint Padding        = Reader.ReadUInt32();

            string FileName = Reader.ReadPaddedString(0x40);

            uint[] Commands = new uint[CommandsLength >> 2];

            for (int Index = 0; Index < Commands.Length; Index++)
            {
                Commands[Index] = Reader.ReadUInt32();
            }

            uint[] OutMap = new uint[7];

            List <uint>  ShaderExecutable = new List <uint>();
            List <ulong> ShaderSwizzles   = new List <ulong>();

            PICACommandReader CmdReader = new PICACommandReader(Commands);

            while (CmdReader.HasCommand)
            {
                PICACommand Cmd = CmdReader.GetCommand();

                uint Param = Cmd.Parameters[0];

                int Stage = ((int)Cmd.Register >> 3) & 7;

                if (Stage >= 6)
                {
                    Stage -= 2;
                }

                switch (Cmd.Register)
                {
                /* Shader */

                case PICARegister.GPUREG_SH_OUTMAP_O0: OutMap[0] = Param; break;

                case PICARegister.GPUREG_SH_OUTMAP_O1: OutMap[1] = Param; break;

                case PICARegister.GPUREG_SH_OUTMAP_O2: OutMap[2] = Param; break;

                case PICARegister.GPUREG_SH_OUTMAP_O3: OutMap[3] = Param; break;

                case PICARegister.GPUREG_SH_OUTMAP_O4: OutMap[4] = Param; break;

                case PICARegister.GPUREG_SH_OUTMAP_O5: OutMap[5] = Param; break;

                case PICARegister.GPUREG_SH_OUTMAP_O6: OutMap[6] = Param; break;

                /* Fragment Shader */

                case PICARegister.GPUREG_TEXENV0_SOURCE:
                case PICARegister.GPUREG_TEXENV1_SOURCE:
                case PICARegister.GPUREG_TEXENV2_SOURCE:
                case PICARegister.GPUREG_TEXENV3_SOURCE:
                case PICARegister.GPUREG_TEXENV4_SOURCE:
                case PICARegister.GPUREG_TEXENV5_SOURCE:
                    TexEnvStages[Stage].Source = new PICATexEnvSource(Param);
                    break;

                case PICARegister.GPUREG_TEXENV0_OPERAND:
                case PICARegister.GPUREG_TEXENV1_OPERAND:
                case PICARegister.GPUREG_TEXENV2_OPERAND:
                case PICARegister.GPUREG_TEXENV3_OPERAND:
                case PICARegister.GPUREG_TEXENV4_OPERAND:
                case PICARegister.GPUREG_TEXENV5_OPERAND:
                    TexEnvStages[Stage].Operand = new PICATexEnvOperand(Param);
                    break;

                case PICARegister.GPUREG_TEXENV0_COMBINER:
                case PICARegister.GPUREG_TEXENV1_COMBINER:
                case PICARegister.GPUREG_TEXENV2_COMBINER:
                case PICARegister.GPUREG_TEXENV3_COMBINER:
                case PICARegister.GPUREG_TEXENV4_COMBINER:
                case PICARegister.GPUREG_TEXENV5_COMBINER:
                    TexEnvStages[Stage].Combiner = new PICATexEnvCombiner(Param);
                    break;

                case PICARegister.GPUREG_TEXENV0_COLOR:
                case PICARegister.GPUREG_TEXENV1_COLOR:
                case PICARegister.GPUREG_TEXENV2_COLOR:
                case PICARegister.GPUREG_TEXENV3_COLOR:
                case PICARegister.GPUREG_TEXENV4_COLOR:
                case PICARegister.GPUREG_TEXENV5_COLOR:
                    TexEnvStages[Stage].Color = new RGBA(Param);
                    break;

                case PICARegister.GPUREG_TEXENV0_SCALE:
                case PICARegister.GPUREG_TEXENV1_SCALE:
                case PICARegister.GPUREG_TEXENV2_SCALE:
                case PICARegister.GPUREG_TEXENV3_SCALE:
                case PICARegister.GPUREG_TEXENV4_SCALE:
                case PICARegister.GPUREG_TEXENV5_SCALE:
                    TexEnvStages[Stage].Scale = new PICATexEnvScale(Param);
                    break;

                case PICARegister.GPUREG_TEXENV_UPDATE_BUFFER: PICATexEnvStage.SetUpdateBuffer(TexEnvStages, Param); break;

                case PICARegister.GPUREG_TEXENV_BUFFER_COLOR: TexEnvBufferColor = new RGBA(Param); break;

                /* Geometry Shader */

                case PICARegister.GPUREG_GSH_ENTRYPOINT:
                    if (GeoShader == null)
                    {
                        GeoShader = new ShaderProgram();
                    }

                    GeoShader.MainOffset = Param & 0xffff;
                    break;

                /* Vertex Shader */

                case PICARegister.GPUREG_VSH_CODETRANSFER_DATA0:
                case PICARegister.GPUREG_VSH_CODETRANSFER_DATA1:
                case PICARegister.GPUREG_VSH_CODETRANSFER_DATA2:
                case PICARegister.GPUREG_VSH_CODETRANSFER_DATA3:
                case PICARegister.GPUREG_VSH_CODETRANSFER_DATA4:
                case PICARegister.GPUREG_VSH_CODETRANSFER_DATA5:
                case PICARegister.GPUREG_VSH_CODETRANSFER_DATA6:
                case PICARegister.GPUREG_VSH_CODETRANSFER_DATA7:
                    ShaderExecutable.AddRange(Cmd.Parameters);
                    break;

                case PICARegister.GPUREG_VSH_OPDESCS_DATA0:
                case PICARegister.GPUREG_VSH_OPDESCS_DATA1:
                case PICARegister.GPUREG_VSH_OPDESCS_DATA2:
                case PICARegister.GPUREG_VSH_OPDESCS_DATA3:
                case PICARegister.GPUREG_VSH_OPDESCS_DATA4:
                case PICARegister.GPUREG_VSH_OPDESCS_DATA5:
                case PICARegister.GPUREG_VSH_OPDESCS_DATA6:
                case PICARegister.GPUREG_VSH_OPDESCS_DATA7:
                    for (int i = 0; i < Cmd.Parameters.Length; i++)
                    {
                        ShaderSwizzles.Add(Cmd.Parameters[i]);
                    }
                    break;

                case PICARegister.GPUREG_VSH_ENTRYPOINT:
                    if (VtxShader == null)
                    {
                        VtxShader = new ShaderProgram();
                    }

                    VtxShader.MainOffset = Param & 0xffff;
                    break;
                }
            }

            Executable = ShaderExecutable.ToArray();
            Swizzles   = ShaderSwizzles.ToArray();

            for (int i = 0; i < OutMap.Length; i++)
            {
                if (OutMap[i] == 0)
                {
                    continue;
                }

                ShaderOutputReg Reg = new ShaderOutputReg();

                for (int j = 0; j < 4; j++)
                {
                    uint Value = (OutMap[i] >> j * 8) & 0x1f;

                    if (Value != 0x1f)
                    {
                        Reg.Mask |= 1u << j;

                        if (Value < 0x4)
                        {
                            Reg.Name = ShaderOutputRegName.Position;
                        }
                        else if (Value < 0x8)
                        {
                            Reg.Name = ShaderOutputRegName.QuatNormal;
                        }
                        else if (Value < 0xc)
                        {
                            Reg.Name = ShaderOutputRegName.Color;
                        }
                        else if (Value < 0xe)
                        {
                            Reg.Name = ShaderOutputRegName.TexCoord0;
                        }
                        else if (Value < 0x10)
                        {
                            Reg.Name = ShaderOutputRegName.TexCoord1;
                        }
                        else if (Value < 0x11)
                        {
                            Reg.Name = ShaderOutputRegName.TexCoord0W;
                        }
                        else if (Value < 0x12)
                        {
                            Reg.Name = ShaderOutputRegName.Generic;
                        }
                        else if (Value < 0x16)
                        {
                            Reg.Name = ShaderOutputRegName.View;
                        }
                        else if (Value < 0x18)
                        {
                            Reg.Name = ShaderOutputRegName.TexCoord2;
                        }
                        else
                        {
                            Reg.Name = ShaderOutputRegName.Generic;
                        }
                    }
                }

                if (VtxShader != null)
                {
                    VtxShader.OutputRegs[i] = Reg;
                }

                if (GeoShader != null)
                {
                    GeoShader.OutputRegs[i] = Reg;
                }
            }

            HashSet <uint> Dsts = new HashSet <uint>();

            uint LblId = 0;

            for (uint i = 0; i < Executable.Length; i++)
            {
                ShaderOpCode OpCode = (ShaderOpCode)(Executable[i] >> 26);

                if (OpCode == ShaderOpCode.Call ||
                    OpCode == ShaderOpCode.CallC ||
                    OpCode == ShaderOpCode.CallU ||
                    OpCode == ShaderOpCode.JmpC ||
                    OpCode == ShaderOpCode.JmpU)
                {
                    uint Dst = (Executable[i] >> 10) & 0xfff;

                    if (!Dsts.Contains(Dst))
                    {
                        Dsts.Add(Dst);

                        string Name = "label_" + Dst.ToString("x4");

                        ShaderLabel Label = new ShaderLabel()
                        {
                            Id     = LblId++,
                            Offset = Dst,
                            Length = 0,
                            Name   = Name
                        };

                        if (VtxShader != null)
                        {
                            VtxShader.Labels.Add(Label);
                        }

                        if (GeoShader != null)
                        {
                            GeoShader.Labels.Add(Label);
                        }
                    }
                }
            }

            MakeArray(VtxShader?.Vec4Uniforms, "v_c");
            MakeArray(GeoShader?.Vec4Uniforms, "g_c");

            FindProgramEnd(VtxShader, Executable);
            FindProgramEnd(GeoShader, Executable);

            VtxShaderUniforms = CmdReader.GetAllVertexShaderUniforms();
            GeoShaderUniforms = CmdReader.GetAllGeometryShaderUniforms();
        }
コード例 #8
0
        private bool open(string fileName)
        {
            using (FileStream data = new FileStream(fileName, FileMode.Open))
            {
                BinaryReader input = new BinaryReader(data);

                string magic = IOUtils.readString(input, 0);
                if (magic != "BCH")
                {
                    return(false);
                }
                currentFile = fileName;
                data.Seek(4, SeekOrigin.Current);
                byte   backwardCompatibility = input.ReadByte();
                byte   forwardCompatibility  = input.ReadByte();
                ushort version = input.ReadUInt16();

                uint mainHeaderOffset      = input.ReadUInt32();
                uint stringTableOffset     = input.ReadUInt32();
                uint gpuCommandsOffset     = input.ReadUInt32();
                uint dataOffset            = input.ReadUInt32();
                uint dataExtendedOffset    = backwardCompatibility > 0x20 ? input.ReadUInt32() : 0;
                uint relocationTableOffset = input.ReadUInt32();

                uint mainHeaderLength      = input.ReadUInt32();
                uint stringTableLength     = input.ReadUInt32();
                uint gpuCommandsLength     = input.ReadUInt32();
                uint dataLength            = input.ReadUInt32();
                uint dataExtendedLength    = backwardCompatibility > 0x20 ? input.ReadUInt32() : 0;
                uint relocationTableLength = input.ReadUInt32();

                data.Seek(mainHeaderOffset, SeekOrigin.Begin);
                uint modelsPointerTableOffset  = input.ReadUInt32() + mainHeaderOffset;
                uint modelsPointerTableEntries = input.ReadUInt32();

                data.Seek(mainHeaderOffset + 0x24, SeekOrigin.Begin);
                uint texturesPointerTableOffset  = input.ReadUInt32() + mainHeaderOffset;
                uint texturesPointerTableEntries = input.ReadUInt32();

                bch = new loadedBCH();

                //Textures
                for (int index = 0; index < texturesPointerTableEntries; index++)
                {
                    data.Seek(texturesPointerTableOffset + (index * 4), SeekOrigin.Begin);
                    data.Seek(input.ReadUInt32() + mainHeaderOffset, SeekOrigin.Begin);

                    loadedTexture tex;
                    tex.modified             = false;
                    tex.gpuCommandsOffset    = input.ReadUInt32() + gpuCommandsOffset;
                    tex.gpuCommandsWordCount = input.ReadUInt32();
                    data.Seek(0x14, SeekOrigin.Current);
                    uint   textureNameOffset = input.ReadUInt32();
                    string textureName       = IOUtils.readString(input, textureNameOffset + stringTableOffset);

                    data.Seek(tex.gpuCommandsOffset, SeekOrigin.Begin);
                    PICACommandReader textureCommands = new PICACommandReader(data, tex.gpuCommandsWordCount);

                    tex.offset = textureCommands.getTexUnit0Address() + dataOffset;
                    RenderBase.OTextureFormat fmt = textureCommands.getTexUnit0Format();
                    Size textureSize = textureCommands.getTexUnit0Size();
                    switch (fmt)
                    {
                    case RenderBase.OTextureFormat.rgba8: tex.length = (textureSize.Width * textureSize.Height) * 4; break;

                    case RenderBase.OTextureFormat.rgb8: tex.length = (textureSize.Width * textureSize.Height) * 3; break;

                    case RenderBase.OTextureFormat.rgba5551: tex.length = (textureSize.Width * textureSize.Height) * 2; break;

                    case RenderBase.OTextureFormat.rgb565: tex.length = (textureSize.Width * textureSize.Height) * 2; break;

                    case RenderBase.OTextureFormat.rgba4: tex.length = (textureSize.Width * textureSize.Height) * 2; break;

                    case RenderBase.OTextureFormat.la8: tex.length = (textureSize.Width * textureSize.Height) * 2; break;

                    case RenderBase.OTextureFormat.hilo8: tex.length = (textureSize.Width * textureSize.Height) * 2; break;

                    case RenderBase.OTextureFormat.l8: tex.length = textureSize.Width * textureSize.Height; break;

                    case RenderBase.OTextureFormat.a8: tex.length = textureSize.Width * textureSize.Height; break;

                    case RenderBase.OTextureFormat.la4: tex.length = textureSize.Width * textureSize.Height; break;

                    case RenderBase.OTextureFormat.l4: tex.length = (textureSize.Width * textureSize.Height) >> 1; break;

                    case RenderBase.OTextureFormat.a4: tex.length = (textureSize.Width * textureSize.Height) >> 1; break;

                    case RenderBase.OTextureFormat.etc1: tex.length = (textureSize.Width * textureSize.Height) >> 1; break;

                    case RenderBase.OTextureFormat.etc1a4: tex.length = textureSize.Width * textureSize.Height; break;

                    default: throw new Exception("OBCHTextureReplacer: Invalid texture format on BCH!");
                    }

                    while ((tex.length & 0x7f) > 0)
                    {
                        tex.length++;
                    }

                    data.Seek(tex.offset, SeekOrigin.Begin);
                    byte[] buffer = new byte[textureSize.Width * textureSize.Height * 4];
                    input.Read(buffer, 0, buffer.Length);
                    Bitmap texture = TextureCodec.decode(
                        buffer,
                        textureSize.Width,
                        textureSize.Height,
                        fmt);

                    tex.texture = new RenderBase.OTexture(texture, textureName);

                    bch.textures.Add(tex);
                }

                //Materials
                for (int mdlIndex = 0; mdlIndex < modelsPointerTableEntries; mdlIndex++)
                {
                    data.Seek(modelsPointerTableOffset + (mdlIndex * 4), SeekOrigin.Begin);
                    data.Seek(input.ReadUInt32() + mainHeaderOffset, SeekOrigin.Begin);
                    data.Seek(0x34, SeekOrigin.Current);

                    uint materialsTableOffset  = input.ReadUInt32() + mainHeaderOffset;
                    uint materialsTableEntries = input.ReadUInt32();

                    for (int index = 0; index < materialsTableEntries; index++)
                    {
                        if (backwardCompatibility < 0x21)
                        {
                            data.Seek(materialsTableOffset + (index * 0x58), SeekOrigin.Begin);
                        }
                        else
                        {
                            data.Seek(materialsTableOffset + (index * 0x2c), SeekOrigin.Begin);
                        }

                        loadedMaterial mat;

                        data.Seek(0x10, SeekOrigin.Current);
                        mat.gpuCommandsOffset    = input.ReadUInt32() + gpuCommandsOffset;
                        mat.gpuCommandsWordCount = input.ReadUInt32();

                        if (backwardCompatibility < 0x21)
                        {
                            data.Seek(0x30, SeekOrigin.Current);
                        }
                        else
                        {
                            data.Seek(4, SeekOrigin.Current);
                        }

                        uint texture0Offset = input.ReadUInt32() + stringTableOffset;
                        uint texture1Offset = input.ReadUInt32() + stringTableOffset;
                        uint texture2Offset = input.ReadUInt32() + stringTableOffset;

                        mat.texture0 = IOUtils.readString(input, texture0Offset);
                        mat.texture1 = IOUtils.readString(input, texture1Offset);
                        mat.texture2 = IOUtils.readString(input, texture2Offset);

                        bch.materials.Add(mat);
                    }
                }

                bch.mainHeaderOffset      = mainHeaderOffset;
                bch.gpuCommandsOffset     = gpuCommandsOffset;
                bch.dataOffset            = dataOffset;
                bch.relocationTableOffset = relocationTableOffset;
                bch.relocationTableLength = relocationTableLength;
            }

            updateTexturesList();
            return(true);
        }