Пример #1
0
        public override void Read(string filename)
        {
            bchHeader header = new bchHeader();
            FileData  f      = new FileData(filename);

            f.Endian = System.IO.Endianness.Little;

            f.skip(4);
            header.backwardCompatibility = f.readByte();
            header.forwardCompatibility  = f.readByte();
            header.version = f.readUShort();

            header.mainHeaderOffset  = f.readInt();
            header.stringTableOffset = f.readInt();
            header.gpuCommandsOffset = f.readInt();
            header.dataOffset        = f.readInt();
            if (header.backwardCompatibility > 0x20)
            {
                header.dataExtendedOffset = f.readInt();
            }
            header.relocationTableOffset = f.readInt();

            header.mainHeaderLength  = f.readInt();
            header.stringTableLength = f.readInt();
            header.gpuCommandsLength = f.readInt();
            header.dataLength        = f.readInt();
            if (header.backwardCompatibility > 0x20)
            {
                header.dataExtendedLength = f.readInt();
            }
            header.relocationTableLength = f.readInt();

            header.uninitializedDataSectionLength        = f.readInt();
            header.uninitializedDescriptionSectionLength = f.readInt();

            if (header.backwardCompatibility > 7)
            {
                header.flags        = f.readUShort();
                header.addressCount = f.readUShort();
            }

            // Relocation table
            for (int i = 0; i < header.relocationTableLength; i += 4)
            {
                f.seek(header.relocationTableOffset + i);
                int  val  = f.readInt();
                int  off  = val & 0x1FFFFFF;
                byte flag = (byte)(val >> 25);

                switch (flag)
                {
                case 0:
                    f.seek((off * 4) + header.mainHeaderOffset);
                    f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.mainHeaderOffset);
                    break;

                case 1:
                    f.seek(off + header.mainHeaderOffset);
                    f.writeInt((off) + header.mainHeaderOffset, f.readInt() + header.stringTableOffset);
                    break;

                case 2:
                    f.seek((off * 4) + header.mainHeaderOffset);
                    f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.gpuCommandsOffset);
                    break;

                case 0xc:
                    f.seek((off * 4) + header.mainHeaderOffset);
                    f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.dataOffset);
                    break;
                }

                f.seek((off * 4) + header.gpuCommandsOffset);
                if (header.backwardCompatibility < 6)
                {
                    switch (flag)
                    {
                    case 0x23: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Texture

                    case 0x25: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Vertex

                    //case 0x26: f.writeInt((off * 4) + header.gpuCommandsOffset, ((f.readInt() + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                    case 0x27: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break;     //Index 8 bits mode
                    }
                }
                else if (header.backwardCompatibility < 8)
                {
                    switch (flag)
                    {
                    case 0x24: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Texture

                    case 0x26: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Vertex

                    //case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                    case 0x28: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break;     //Index 8 bits mode
                    }
                }
                else if (header.backwardCompatibility < 0x21)
                {
                    switch (flag)
                    {
                    case 0x25: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Texture

                    case 0x27: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Vertex

                    //case 0x28: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                    case 0x29: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break;     //Index 8 bits mode
                    }
                }
                else
                {
                    switch (flag)
                    {
                    case 0x25: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Texture

                    case 0x26: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Vertex relative to Data Offset

                    //case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Offset
                    case 0x28: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Offset

                    case 0x2b: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataExtendedOffset); break;        //Vertex relative to Data Extended Offset

                    //case 0x2c: writer.Write(((peek(input) + header.dataExtendedOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Extended Offset
                    case 0x2d: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataExtendedOffset) & 0x7fffffff); break;     //Index 8 bits mode relative to Data Extended Offset
                    }
                }
            }


            // Content Header
            f.seek(header.mainHeaderOffset);
            bchContentHeader content = new bchContentHeader();

            {
                content.modelsPointerTableOffset        = f.readInt();
                content.modelsPointerTableEntries       = f.readInt();
                content.modelsNameOffset                = f.readInt();
                content.materialsPointerTableOffset     = f.readInt();
                content.materialsPointerTableEntries    = f.readInt();
                content.materialsNameOffset             = f.readInt();
                content.shadersPointerTableOffset       = f.readInt();
                content.shadersPointerTableEntries      = f.readInt();
                content.shadersNameOffset               = f.readInt();
                content.texturesPointerTableOffset      = f.readInt();
                content.texturesPointerTableEntries     = f.readInt();
                content.texturesNameOffset              = f.readInt();
                content.materialsLUTPointerTableOffset  = f.readInt();
                content.materialsLUTPointerTableEntries = f.readInt();
                content.materialsLUTNameOffset          = f.readInt();
                content.lightsPointerTableOffset        = f.readInt();
                content.lightsPointerTableEntries       = f.readInt();
                content.lightsNameOffset                = f.readInt();
                content.camerasPointerTableOffset       = f.readInt();
                content.camerasPointerTableEntries      = f.readInt();
                content.camerasNameOffset               = f.readInt();
                content.fogsPointerTableOffset          = f.readInt();
                content.fogsPointerTableEntries         = f.readInt();
                content.fogsNameOffset = f.readInt();
                content.skeletalAnimationsPointerTableOffset    = f.readInt();
                content.skeletalAnimationsPointerTableEntries   = f.readInt();
                content.skeletalAnimationsNameOffset            = f.readInt();
                content.materialAnimationsPointerTableOffset    = f.readInt();
                content.materialAnimationsPointerTableEntries   = f.readInt();
                content.materialAnimationsNameOffset            = f.readInt();
                content.visibilityAnimationsPointerTableOffset  = f.readInt();
                content.visibilityAnimationsPointerTableEntries = f.readInt();
                content.visibilityAnimationsNameOffset          = f.readInt();
                content.lightAnimationsPointerTableOffset       = f.readInt();
                content.lightAnimationsPointerTableEntries      = f.readInt();
                content.lightAnimationsNameOffset           = f.readInt();
                content.cameraAnimationsPointerTableOffset  = f.readInt();
                content.cameraAnimationsPointerTableEntries = f.readInt();
                content.cameraAnimationsNameOffset          = f.readInt();
                content.fogAnimationsPointerTableOffset     = f.readInt();
                content.fogAnimationsPointerTableEntries    = f.readInt();
                content.fogAnimationsNameOffset             = f.readInt();
                content.scenePointerTableOffset             = f.readInt();
                content.scenePointerTableEntries            = f.readInt();
                content.sceneNameOffset = f.readInt();
            }


            //Skeletal animation
            for (int index1 = 0; index1 < content.skeletalAnimationsPointerTableEntries; index1++)
            {
                f.seek(content.skeletalAnimationsPointerTableOffset + (index1 * 4));
                int dataOffset = f.readInt();
                f.seek(dataOffset);


                string skeletalAnimationName = f.readString(f.readInt(), -1);
                int    animationFlags        = f.readInt();
                //int skeletalAnimationloopMode = f.readByte();  //pas ça du tout
                float skeletalAnimationframeSize = f.readFloat();
                int   boneTableOffset            = f.readInt();
                int   boneTableEntries           = f.readInt();
                int   metaDataPointerOffset      = f.readInt();

                //Debug.WriteLine("Animation Name: " + skeletalAnimationName);
                //Debug.WriteLine("BonetableOffset: " + boneTableOffset.ToString("X"));
                //Debug.WriteLine("BonetableEntry: " + boneTableEntries.ToString("X"));

                for (int i = 0; i < boneTableEntries; i++)
                {
                    f.seek(boneTableOffset + (i * 4));
                    int offset = f.readInt();

                    OSkeletalAnimationBone bone = new OSkeletalAnimationBone();

                    f.seek(offset);
                    bone.name = f.readString(f.readInt(), -1);
                    Console.WriteLine("Bone Name: " + bone.name);
                    int animationTypeFlags = f.readInt();
                    int flags = f.readInt();

                    OSegmentType segmentType = (OSegmentType)((animationTypeFlags >> 16) & 0xf);
                    switch (segmentType)
                    {
                    case OSegmentType.transform:
                        f.seek(offset + 0x18);

                        int notExistMask = 0x80000;
                        int constantMask = 0x200;

                        for (int j = 0; j < 2; j++)
                        {
                            for (int axis = 0; axis < 3; axis++)
                            {
                                bool notExist = (flags & notExistMask) > 0;
                                bool constant = (flags & constantMask) > 0;

                                OAnimationKeyFrameGroup frame = new OAnimationKeyFrameGroup();
                                frame.exists = !notExist;
                                if (frame.exists)
                                {
                                    if (constant)
                                    {
                                        frame.interpolation = OInterpolationMode.linear;
                                        frame.keyFrames.Add(new OAnimationKeyFrame(f.readFloat(), 0));
                                    }
                                    else
                                    {
                                        int frameOffset = f.readInt();
                                        int position    = f.pos();
                                        f.seek(frameOffset);
                                        //getAnimationKeyFrame(input, frame);
                                        f.seek(position);
                                    }
                                }
                                else
                                {
                                    f.seek(f.pos() + 0x04);
                                }

                                if (j == 0)
                                {
                                    switch (axis)
                                    {
                                    case 0: bone.rotationX = frame; break;

                                    case 1: bone.rotationY = frame; break;

                                    case 2: bone.rotationZ = frame; break;
                                    }
                                }
                                else
                                {
                                    switch (axis)
                                    {
                                    case 0: bone.translationX = frame; break;

                                    case 1: bone.translationY = frame; break;

                                    case 2: bone.translationZ = frame; break;
                                    }
                                }

                                notExistMask <<= 1;
                                constantMask <<= 1;
                            }

                            constantMask <<= 1;
                        }

                        break;

                    case OSegmentType.transformQuaternion:
                        bone.isFrameFormat = true;

                        int scaleOffset       = f.readInt();
                        int rotationOffset    = f.readInt();
                        int translationOffset = f.readInt();

                        if ((flags & 0x20) == 0)
                        {
                            bone.scale.exists = true;
                            f.seek(scaleOffset);

                            if ((flags & 4) > 0)
                            {
                                bone.scale.vector.Add(new Vector4(
                                                          f.readFloat(),
                                                          f.readFloat(),
                                                          f.readFloat(),
                                                          0));
                            }
                            else
                            {
                                bone.scale.startFrame = f.readFloat();
                                bone.scale.endFrame   = f.readFloat();

                                int scaleFlags      = f.readInt();
                                int scaleDataOffset = f.readInt();
                                int scaleEntries    = f.readInt();

                                f.seek(scaleDataOffset);
                                for (int j = 0; j < scaleEntries; j++)
                                {
                                    bone.scale.vector.Add(new Vector4(
                                                              f.readFloat(),
                                                              f.readFloat(),
                                                              f.readFloat(),
                                                              0));
                                }
                            }
                        }

                        if ((flags & 0x10) == 0)
                        {
                            bone.rotationQuaternion.exists = true;
                            f.seek(rotationOffset);

                            if ((flags & 2) > 0)
                            {
                                bone.rotationQuaternion.vector.Add(new Vector4(
                                                                       f.readFloat(),
                                                                       f.readFloat(),
                                                                       f.readFloat(),
                                                                       f.readFloat()));
                            }
                            else
                            {
                                bone.rotationQuaternion.startFrame = f.readFloat();
                                bone.rotationQuaternion.endFrame   = f.readFloat();

                                int rotationFlags      = f.readInt();
                                int rotationDataOffset = f.readInt();
                                int rotationEntries    = f.readInt();

                                f.seek(rotationDataOffset);
                                for (int j = 0; j < rotationEntries; j++)
                                {
                                    bone.rotationQuaternion.vector.Add(new Vector4(
                                                                           f.readFloat(),
                                                                           f.readFloat(),
                                                                           f.readFloat(),
                                                                           f.readFloat()));
                                }
                            }
                        }

                        if ((flags & 8) == 0)
                        {
                            bone.translation.exists = true;
                            f.seek(translationOffset);

                            if ((flags & 1) > 0)
                            {
                                bone.translation.vector.Add(new Vector4(
                                                                f.readFloat(),
                                                                f.readFloat(),
                                                                f.readFloat(),
                                                                0));
                            }
                            else
                            {
                                bone.translation.startFrame = f.readFloat();
                                bone.translation.endFrame   = f.readFloat();

                                int translationFlags      = f.readInt();
                                int translationDataOffset = f.readInt();
                                int translationEntries    = f.readInt();

                                f.seek(translationDataOffset);
                                for (int j = 0; j < translationEntries; j++)
                                {
                                    bone.translation.vector.Add(new Vector4(
                                                                    f.readFloat(),
                                                                    f.readFloat(),
                                                                    f.readFloat(),
                                                                    0));
                                }
                            }
                        }

                        break;

                    case OSegmentType.transformMatrix:
                        bone.isFullBakedFormat = true;

                        f.readInt();
                        f.readInt();
                        int matrixOffset = f.readInt();
                        int entries      = f.readInt();

                        f.seek(matrixOffset);
                        for (int j = 0; j < entries; j++)
                        {
                            /*OMatrix transform = new OMatrix();
                             * transform.M11 = f.readFloat();
                             * transform.M21 = f.readFloat();
                             * transform.M31 = f.readFloat();
                             * transform.M41 = f.readFloat();
                             *
                             * transform.M12 = f.readFloat();
                             * transform.M22 = f.readFloat();
                             * transform.M32 = f.readFloat();
                             * transform.M42 = f.readFloat();
                             *
                             * transform.M13 = f.readFloat();
                             * transform.M23 = f.readFloat();
                             * transform.M33 = f.readFloat();
                             * transform.M43 = f.readFloat();
                             *
                             * bone.transform.Add(transform);*/
                        }

                        break;

                    default: throw new Exception(string.Format("BCH: Unknow Segment Type {0} on Skeletal Animation bone {1}! STOP!", segmentType, bone.name));
                    }

                    //skeletalAnimation.bone.Add(bone);
                }
            }

            //Shaders (unused for now, until someone wants to add them)
            for (int index = 0; index < content.shadersPointerTableEntries; index++)
            {
                f.seek(content.shadersPointerTableOffset + (index * 4));
                int dataOffset = f.readInt();
                f.seek(dataOffset);

                int shaderDataOffset = f.readInt();
                int shaderDataLength = f.readInt();
            }

            // Textures
            // WIP Section
            for (int index = 0; index < content.texturesPointerTableEntries; index++)
            {
                f.seek(content.texturesPointerTableOffset + (index * 4));
                int dOffset = f.readInt();
                f.seek(dOffset);

                int textureCommandsOffset    = f.readInt();
                int textureCommandsWordCount = f.readInt();

                f.seek(f.pos() + 0x14);
                String textureName = f.readString(f.readInt(), -1);
                f.seek(textureCommandsOffset);
                BCH_Texture tex = new BCH_Texture();
                textures.Add(textureName, tex);

                tex.Height = f.readUShort();
                tex.Width  = f.readUShort();
                f.skip(12);
                int doffset = f.readInt();
                f.skip(4);
                tex.type = f.readInt();
                tex.data = f.getSection(doffset, f.size() - doffset);

                tex.texture = _3DS.DecodeImage(tex.data, tex.Width, tex.Height, (_3DS.Tex_Formats)tex.type);
                //Texture texture = new Texture2D(tex.texture);
                //tex.display = texture.Id;
            }

            // Model data

            for (int modelIndex = 0; modelIndex < content.modelsPointerTableEntries; modelIndex++)
            {
                f.seek(content.modelsPointerTableOffset + (modelIndex * 4));
                int objectsHeaderOffset = f.readInt();

                // Objects
                f.seek(objectsHeaderOffset);
                BCH_Model model = new BCH_Model();
                models.Add(model);

                model.flags                     = f.readByte();
                model.skeletonScaleType         = f.readByte();
                model.silhouetteMaterialEntries = f.readUShort();

                model.worldTransform = new Matrix4(f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat()
                                                   , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat()
                                                   , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat()
                                                   , 0, 0, 0, 1);

                int materialsTableOffset  = f.readInt();
                int materialsTableEntries = f.readInt();
                int materialsNameOffset   = f.readInt();
                int verticesTableOffset   = f.readInt();
                int verticesTableEntries  = f.readInt();
                f.skip(0x28);
                int    skeletonOffset              = f.readInt();
                int    skeletonEntries             = f.readInt();
                int    skeletonNameOffset          = f.readInt();
                int    objectsNodeVisibilityOffset = f.readInt();
                int    objectsNodeCount            = f.readInt();
                String name = f.readString(f.readInt(), -1);
                int    objectsNodeNameEntries = f.readInt();
                int    objectsNodeNameOffset  = f.readInt();
                f.readInt(); //0x0
                int metaDataPointerOffset = f.readInt();

                f.seek(objectsNodeVisibilityOffset);
                int nodeVisibility = f.readInt();

                string[] objectName = new string[objectsNodeNameEntries];
                f.seek(objectsNodeNameOffset);
                int rootReferenceBit = f.readInt(); //Radix tree
                int rootLeftNode     = f.readUShort();
                int rootRightNode    = f.readUShort();
                int rootNameOffset   = f.readInt();

                for (int i = 0; i < objectsNodeNameEntries; i++)
                {
                    int   referenceBit = f.readInt();
                    short leftNode     = f.readShort();
                    short rightNode    = f.readShort();
                    objectName[i] = f.readString(f.readInt(), -1);
                }

                // Materials
                // NOTE: MATERIALS AND OBJECT SECTIONS ARE REALLY MESSY ATM

                String[] materialNames = new String[materialsTableEntries];
                for (int index = 0; index < materialsTableEntries; index++)
                {
                    f.seek(materialsTableOffset + (index * 0x2c));

                    int materialParametersOffset = f.readInt();
                    f.readInt();
                    f.readInt();
                    f.readInt();
                    int textureCommandsOffset    = f.readInt();
                    int textureCommandsWordCount = f.readInt();

                    int materialMapperOffset = f.readInt();
                    materialNames[index] = f.readString(f.readInt(), -1);
                }

                // Object Descriptions...
                // Assumes MBN is already loaded for now
                f.seek(verticesTableOffset);
                List <objDes> objDescriptors = new List <objDes>();
                if (mbn == null)
                {
                    mbn = new Smash_Forge.MBN();
                    for (int index = 0; index < verticesTableEntries; index++)
                    {
                        mbn.mesh.Add(new MBN.Mesh());
                    }
                    mbn.PreRender();
                }
                for (int index = 0; index < mbn.mesh.Count; index++)
                {
                    int i = f.readUShort();
                    if (index > mbn.mesh.Count)
                    {
                        break;
                    }
                    if (i > materialNames.Length)
                    {
                        break;
                    }
                    mbn.mesh[index].texId = textures[materialNames[i]].display;
                    Console.WriteLine("Tex index" + mbn.mesh[index].texId);
                    f.skip(2); // flags
                    int nameId = f.readUShort();
                    mbn.mesh[index].Text = objectName[nameId];

                    // node visibility TODO: finish...
                    mbn.mesh[index].Checked = ((nodeVisibility & (1 << nameId)) > 0);

                    mbn.mesh[index].renderPriority = f.readUShort();

                    objDes des = new objDes();
                    objDescriptors.Add(des);
                    des.vshAttBufferCommandOffset = f.readInt();
                    des.vshAttBufferCommandCount  = f.readInt();
                    des.faceOffset = f.readInt();
                    des.faceCount  = f.readInt();
                    des.vshAttBufferCommandOffsetEx = f.readInt();
                    des.vshAttBufferCommandCountEx  = f.readInt();

                    f.skip(12);  // center vector
                    f.skip(4);   // flagsOffset
                    f.skip(4);   // 0?
                    f.readInt(); //bbOffsets[i]
                }

                //Skeleton
                f.seek(skeletonOffset);
                for (int index = 0; index < skeletonEntries; index++)
                {
                    Bone bone      = new Bone(model.skeleton);
                    int  boneFlags = f.readInt();
                    bone.parentIndex = f.readShort();
                    short boneSpace = f.readShort();
                    bone.scale       = new float[3];
                    bone.rotation    = new float[3];
                    bone.position    = new float[3];
                    bone.scale[0]    = f.readFloat();
                    bone.scale[1]    = f.readFloat();
                    bone.scale[2]    = f.readFloat();
                    bone.rotation[0] = f.readFloat();
                    bone.rotation[1] = f.readFloat();
                    bone.rotation[2] = f.readFloat();
                    bone.position[0] = f.readFloat();
                    bone.position[1] = f.readFloat();
                    bone.position[2] = f.readFloat();

                    // bone matrix... not really needed to be stored per say
                    f.skip(4 * 4 * 3);

                    bone.Text = f.readString(f.readInt(), -1);

                    f.skip(4); // Meta data
                    bones.bones.Add(bone);


                    model.skeleton.bones.Add(bone);
                }
                model.skeleton.reset();
                model.skeleton.update();
            }
        }
Пример #2
0
        /// <summary>
        ///     Loads a BCH file.
        ///     Note that BCH must start at offset 0x0 (don't try using it for BCHs inside containers).
        /// </summary>
        /// <param name="data">Memory Stream of the BCH file. The Stream will not be usable after</param>
        /// <returns></returns>
        public static RenderBase.OModelGroup load(MemoryStream data)
        {
            BinaryReader input = new BinaryReader(data);
            BinaryWriter writer = new BinaryWriter(data);

            RenderBase.OModelGroup models = new RenderBase.OModelGroup();

            //Primary header
            bchHeader header = new bchHeader();
            header.magic = IOUtils.readString(input, 0);
            data.Seek(4, SeekOrigin.Current);
            header.backwardCompatibility = input.ReadByte();
            header.forwardCompatibility = input.ReadByte();
            header.version = input.ReadUInt16();

            header.mainHeaderOffset = input.ReadUInt32();
            header.stringTableOffset = input.ReadUInt32();
            header.gpuCommandsOffset = input.ReadUInt32();
            header.dataOffset = input.ReadUInt32();
            if (header.backwardCompatibility > 0x20) header.dataExtendedOffset = input.ReadUInt32();
            header.relocationTableOffset = input.ReadUInt32();

            header.mainHeaderLength = input.ReadUInt32();
            header.stringTableLength = input.ReadUInt32();
            header.gpuCommandsLength = input.ReadUInt32();
            header.dataLength = input.ReadUInt32();
            if (header.backwardCompatibility > 0x20) header.dataExtendedLength = input.ReadUInt32();
            header.relocationTableLength = input.ReadUInt32();

            header.uninitializedDataSectionLength = input.ReadUInt32();
            header.uninitializedDescriptionSectionLength = input.ReadUInt32();

            if (header.backwardCompatibility > 7)
            {
                header.flags = input.ReadUInt16();
                header.addressCount = input.ReadUInt16();
            }

            //Transform relative offsets to absolute offsets, also add extra bits if necessary.
            //The game does this on RAM after the BCH is loaded, so offsets to data is absolute and points to VRAM.
            for (uint o = header.relocationTableOffset; o < header.relocationTableOffset + header.relocationTableLength; o += 4)
            {
                data.Seek(o, SeekOrigin.Begin);
                uint value = input.ReadUInt32();
                uint offset = value & 0x1ffffff;
                byte flags = (byte)(value >> 25);

                switch (flags)
                {
                    case 0:
                        data.Seek((offset * 4) + header.mainHeaderOffset, SeekOrigin.Begin);
                        writer.Write(peek(input) + header.mainHeaderOffset);
                        break;

                    case 1:
                        data.Seek(offset + header.mainHeaderOffset, SeekOrigin.Begin);
                        writer.Write(peek(input) + header.stringTableOffset);
                        break;

                    case 2:
                        data.Seek((offset * 4) + header.mainHeaderOffset, SeekOrigin.Begin);
                        writer.Write(peek(input) + header.gpuCommandsOffset);
                        break;

                    case 7:
                    case 0xc:
                        data.Seek((offset * 4) + header.mainHeaderOffset, SeekOrigin.Begin);
                        writer.Write(peek(input) + header.dataOffset);
                        break;
                }

                //The moron that designed the format used different flags on different versions, instead of keeping compatibility.
                data.Seek((offset * 4) + header.gpuCommandsOffset, SeekOrigin.Begin);
                if (header.backwardCompatibility < 6)
                {
                    switch (flags)
                    {
                        case 0x23: writer.Write(peek(input) + header.dataOffset); break; //Texture
                        case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Vertex
                        case 0x26: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                        case 0x27: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode
                    }
                }
                else if (header.backwardCompatibility < 8)
                {
                    switch (flags)
                    {
                        case 0x24: writer.Write(peek(input) + header.dataOffset); break; //Texture
                        case 0x26: writer.Write(peek(input) + header.dataOffset); break; //Vertex
                        case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                        case 0x28: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode
                    }
                }
                else if (header.backwardCompatibility < 0x21)
                {
                    switch (flags)
                    {
                        case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Texture
                        case 0x27: writer.Write(peek(input) + header.dataOffset); break; //Vertex
                        case 0x28: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                        case 0x29: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode
                    }
                }
                else
                {
                    switch (flags)
                    {
                        case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Texture
                        case 0x26: writer.Write(peek(input) + header.dataOffset); break; //Vertex relative to Data Offset
                        case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Offset
                        case 0x28: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Offset
                        case 0x2b: writer.Write(peek(input) + header.dataExtendedOffset); break; //Vertex relative to Data Extended Offset
                        case 0x2c: writer.Write(((peek(input) + header.dataExtendedOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Extended Offset
                        case 0x2d: writer.Write((peek(input) + header.dataExtendedOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Extended Offset
                    }
                }
            }

            //Content header
            data.Seek(header.mainHeaderOffset, SeekOrigin.Begin);
            bchContentHeader contentHeader = new bchContentHeader
            {
                modelsPointerTableOffset = input.ReadUInt32(),
                modelsPointerTableEntries = input.ReadUInt32(),
                modelsNameOffset = input.ReadUInt32(),
                materialsPointerTableOffset = input.ReadUInt32(),
                materialsPointerTableEntries = input.ReadUInt32(),
                materialsNameOffset = input.ReadUInt32(),
                shadersPointerTableOffset = input.ReadUInt32(),
                shadersPointerTableEntries = input.ReadUInt32(),
                shadersNameOffset = input.ReadUInt32(),
                texturesPointerTableOffset = input.ReadUInt32(),
                texturesPointerTableEntries = input.ReadUInt32(),
                texturesNameOffset = input.ReadUInt32(),
                materialsLUTPointerTableOffset = input.ReadUInt32(),
                materialsLUTPointerTableEntries = input.ReadUInt32(),
                materialsLUTNameOffset = input.ReadUInt32(),
                lightsPointerTableOffset = input.ReadUInt32(),
                lightsPointerTableEntries = input.ReadUInt32(),
                lightsNameOffset = input.ReadUInt32(),
                camerasPointerTableOffset = input.ReadUInt32(),
                camerasPointerTableEntries = input.ReadUInt32(),
                camerasNameOffset = input.ReadUInt32(),
                fogsPointerTableOffset = input.ReadUInt32(),
                fogsPointerTableEntries = input.ReadUInt32(),
                fogsNameOffset = input.ReadUInt32(),
                skeletalAnimationsPointerTableOffset = input.ReadUInt32(),
                skeletalAnimationsPointerTableEntries = input.ReadUInt32(),
                skeletalAnimationsNameOffset = input.ReadUInt32(),
                materialAnimationsPointerTableOffset = input.ReadUInt32(),
                materialAnimationsPointerTableEntries = input.ReadUInt32(),
                materialAnimationsNameOffset = input.ReadUInt32(),
                visibilityAnimationsPointerTableOffset = input.ReadUInt32(),
                visibilityAnimationsPointerTableEntries = input.ReadUInt32(),
                visibilityAnimationsNameOffset = input.ReadUInt32(),
                lightAnimationsPointerTableOffset = input.ReadUInt32(),
                lightAnimationsPointerTableEntries = input.ReadUInt32(),
                lightAnimationsNameOffset = input.ReadUInt32(),
                cameraAnimationsPointerTableOffset = input.ReadUInt32(),
                cameraAnimationsPointerTableEntries = input.ReadUInt32(),
                cameraAnimationsNameOffset = input.ReadUInt32(),
                fogAnimationsPointerTableOffset = input.ReadUInt32(),
                fogAnimationsPointerTableEntries = input.ReadUInt32(),
                fogAnimationsNameOffset = input.ReadUInt32(),
                scenePointerTableOffset = input.ReadUInt32(),
                scenePointerTableEntries = input.ReadUInt32(),
                sceneNameOffset = input.ReadUInt32()
            };
            //Note: NameOffset are PATRICIA trees

            //Shaders
            for (int index = 0; index < contentHeader.shadersPointerTableEntries; index++)
            {
                data.Seek(contentHeader.shadersPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                uint shaderDataOffset = input.ReadUInt32();
                uint shaderDataLength = input.ReadUInt32();
            }

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

                uint textureCommandsOffset = input.ReadUInt32();
                uint textureCommandsWordCount = input.ReadUInt32();
                data.Seek(0x14, SeekOrigin.Current);
                string textureName = readString(input);

                data.Seek(textureCommandsOffset, SeekOrigin.Begin);
                PICACommandReader textureCommands = new PICACommandReader(data, textureCommandsWordCount);

                //Note: It have textures for the 3 texture units.
                //The other texture units are used with textureCoordinate1 and 2.
                data.Seek(textureCommands.getTexUnit0Address(), SeekOrigin.Begin);
                Size textureSize = textureCommands.getTexUnit0Size();
                byte[] buffer = new byte[textureSize.Width * textureSize.Height * 4];
                input.Read(buffer, 0, buffer.Length);
                Bitmap texture = TextureCodec.decode(
                    buffer,
                    textureSize.Width,
                    textureSize.Height,
                    textureCommands.getTexUnit0Format());

                models.texture.Add(new RenderBase.OTexture(texture, textureName));
            }

            //LookUp Tables
            for (int index = 0; index < contentHeader.materialsLUTPointerTableEntries; index++)
            {
                data.Seek(contentHeader.materialsLUTPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                input.ReadUInt32();
                uint samplersCount = input.ReadUInt32();
                string name = readString(input);

                RenderBase.OLookUpTable table = new RenderBase.OLookUpTable();
                table.name = name;
                for (int i = 0; i < samplersCount; i++)
                {
                    RenderBase.OLookUpTableSampler sampler = new RenderBase.OLookUpTableSampler();

                    input.ReadUInt32();
                    uint tableOffset = input.ReadUInt32();
                    uint tableWordCount = input.ReadUInt32();
                    sampler.name = readString(input);

                    long dataPosition = data.Position;
                    data.Seek(tableOffset, SeekOrigin.Begin);
                    PICACommandReader lutCommands = new PICACommandReader(data, tableWordCount);
                    sampler.table = lutCommands.getFSHLookUpTable();
                    table.sampler.Add(sampler);

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

                models.lookUpTable.Add(table);
            }

            //Lights
            for (int index = 0; index < contentHeader.lightsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.lightsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OLight light = new RenderBase.OLight();
                light.name = readString(input);
                light.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                light.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                light.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());

                uint lightFlags = input.ReadUInt32();
                switch (lightFlags & 0xf)
                {
                    case 1: light.lightUse = RenderBase.OLightUse.hemiSphere; break;
                    case 2: light.lightUse = RenderBase.OLightUse.ambient; break;
                    case 5:
                    case 6:
                    case 7:
                        light.lightUse = RenderBase.OLightUse.vertex;
                        switch (lightFlags & 0xf)
                        {
                            case 5: light.lightType = RenderBase.OLightType.directional; break;
                            case 6: light.lightType = RenderBase.OLightType.point; break;
                            case 7: light.lightType = RenderBase.OLightType.spot; break;
                        }
                        break;
                    case 9:
                    case 0xa:
                    case 0xb:
                        light.lightUse = RenderBase.OLightUse.fragment;
                        switch (lightFlags & 0xf)
                        {
                            case 9: light.lightType = RenderBase.OLightType.directional; break;
                            case 0xa: light.lightType = RenderBase.OLightType.point; break;
                            case 0xb: light.lightType = RenderBase.OLightType.spot; break;
                        }
                        break;
                    default: Debug.WriteLine(string.Format("BCH: Warning - Unknow Light Flags {0}", lightFlags.ToString("X8"))); break;
                }
                light.isLightEnabled = (lightFlags & 0x100) > 0;
                light.isTwoSideDiffuse = (lightFlags & 0x10000) > 0;
                light.isDistanceAttenuationEnabled = (lightFlags & 0x20000) > 0;
                light.angleSampler.input = (RenderBase.OFragmentSamplerInput)((lightFlags >> 24) & 0xf);
                light.angleSampler.scale = (RenderBase.OFragmentSamplerScale)((lightFlags >> 28) & 0xf);

                input.ReadUInt32();
                switch (light.lightUse)
                {
                    case RenderBase.OLightUse.hemiSphere:
                        light.groundColor = MeshUtils.getColorFloat(input);
                        light.skyColor = MeshUtils.getColorFloat(input);
                        light.direction = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                        light.lerpFactor = input.ReadSingle();
                        break;
                    case RenderBase.OLightUse.ambient: light.ambient = MeshUtils.getColor(input); break;
                    case RenderBase.OLightUse.vertex:
                        light.ambient = MeshUtils.getColorFloat(input);
                        light.diffuse = MeshUtils.getColorFloat(input);
                        light.direction = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                        light.distanceAttenuationConstant = input.ReadSingle();
                        light.distanceAttenuationLinear = input.ReadSingle();
                        light.distanceAttenuationQuadratic = input.ReadSingle();
                        light.spotExponent = input.ReadSingle();
                        light.spotCutoffAngle = input.ReadSingle();
                        break;
                    case RenderBase.OLightUse.fragment:
                        light.ambient = MeshUtils.getColor(input);
                        light.diffuse = MeshUtils.getColor(input);
                        light.specular0 = MeshUtils.getColor(input);
                        light.specular1 = MeshUtils.getColor(input);
                        light.direction = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                        input.ReadUInt32();
                        input.ReadUInt32();
                        light.attenuationStart = input.ReadSingle();
                        light.attenuationEnd = input.ReadSingle();

                        light.distanceSampler.tableName = readString(input);
                        light.distanceSampler.samplerName = readString(input);

                        light.angleSampler.tableName = readString(input);
                        light.angleSampler.samplerName = readString(input);
                        break;
                }

                models.light.Add(light);
            }

            //Cameras
            for (int index = 0; index < contentHeader.camerasPointerTableEntries; index++)
            {
                data.Seek(contentHeader.camerasPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OCamera camera = new RenderBase.OCamera();
                camera.name = readString(input);
                camera.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                camera.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                camera.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());

                uint cameraFlags = input.ReadUInt32();
                camera.isInheritingTargetRotate = (cameraFlags & 0x10000) > 0;
                camera.isInheritingTargetTranslate = (cameraFlags & 0x20000) > 0;
                camera.isInheritingUpRotate = (cameraFlags & 0x40000) > 0;
                camera.view = (RenderBase.OCameraView)(cameraFlags & 0xf);
                camera.projection = (RenderBase.OCameraProjection)((cameraFlags >> 8) & 0xf);

                input.ReadSingle();
                uint viewOffset = input.ReadUInt32();
                uint projectionOffset = input.ReadUInt32();

                data.Seek(viewOffset, SeekOrigin.Begin);
                camera.target = new RenderBase.OVector3();
                camera.rotation = new RenderBase.OVector3();
                camera.upVector = new RenderBase.OVector3();
                RenderBase.OVector3 target = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                switch (camera.view)
                {
                    case RenderBase.OCameraView.aimTarget: camera.target = target; camera.twist = input.ReadSingle(); break;
                    case RenderBase.OCameraView.lookAtTarget: camera.target = target; camera.upVector = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); break;
                    case RenderBase.OCameraView.rotate: camera.rotation = target; break;
                }

                data.Seek(projectionOffset, SeekOrigin.Begin);
                camera.zNear = input.ReadSingle();
                camera.zFar = input.ReadSingle();
                camera.aspectRatio = input.ReadSingle();
                switch (camera.projection)
                {
                    case RenderBase.OCameraProjection.perspective: camera.fieldOfViewY = input.ReadSingle(); break;
                    case RenderBase.OCameraProjection.orthogonal: camera.height = input.ReadSingle(); break;
                }

                models.camera.Add(camera);
            }

            //Fogs
            for (int index = 0; index < contentHeader.fogsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.fogsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OFog fog = new RenderBase.OFog();
                fog.name = readString(input);
                fog.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                fog.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                fog.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());

                uint fogFlags = input.ReadUInt32();
                fog.fogUpdater = (RenderBase.OFogUpdater)(fogFlags & 0xf);
                fog.isZFlip = (fogFlags & 0x100) > 0;
                fog.isAttenuateDistance = (fogFlags & 0x200) > 0;

                fog.fogColor = MeshUtils.getColor(input);

                fog.minFogDepth = input.ReadSingle();
                fog.maxFogDepth = input.ReadSingle();
                fog.fogDensity = input.ReadSingle();

                models.fog.Add(fog);
            }

            //Skeletal Animations
            for (int index = 0; index < contentHeader.skeletalAnimationsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.skeletalAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OSkeletalAnimation skeletalAnimation = new RenderBase.OSkeletalAnimation();

                skeletalAnimation.name = readString(input);
                uint animationFlags = input.ReadUInt32();
                skeletalAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
                skeletalAnimation.frameSize = input.ReadSingle();
                uint boneTableOffset = input.ReadUInt32();
                uint boneTableEntries = input.ReadUInt32();
                uint metaDataPointerOffset = input.ReadUInt32();

                if (metaDataPointerOffset != 0)
                {
                    data.Seek(metaDataPointerOffset, SeekOrigin.Begin);
                    skeletalAnimation.userData = getMetaData(input);
                }

                for (int i = 0; i < boneTableEntries; i++)
                {
                    data.Seek(boneTableOffset + (i * 4), SeekOrigin.Begin);
                    uint offset = input.ReadUInt32();

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

                    data.Seek(offset, SeekOrigin.Begin);
                    bone.name = readString(input);
                    uint animationTypeFlags = input.ReadUInt32();
                    uint flags = input.ReadUInt32();
                    input.ReadUInt32();

                    RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);
                    switch (segmentType)
                    {
                        case RenderBase.OSegmentType.transform:
                            data.Seek(offset + 0x18, SeekOrigin.Begin);

                            uint notExistMask = 0x80000;
                            uint constantMask = 0x200;

                            for (int j = 0; j < 2; j++)
                            {
                                for (int axis = 0; axis < 3; axis++)
                                {
                                    bool notExist = (flags & notExistMask) > 0;
                                    bool constant = (flags & constantMask) > 0;

                                    RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();
                                    frame.exists = !notExist;
                                    if (frame.exists)
                                    {
                                        if (constant)
                                        {
                                            frame.interpolation = RenderBase.OInterpolationMode.linear;
                                            frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0));
                                        }
                                        else
                                        {
                                            uint frameOffset = input.ReadUInt32();
                                            long position = data.Position;
                                            data.Seek(frameOffset, SeekOrigin.Begin);
                                            getAnimationKeyFrame(input, frame);
                                            data.Seek(position, SeekOrigin.Begin);
                                        }
                                    }
                                    else
                                        data.Seek(4, SeekOrigin.Current);

                                    if (j == 0)
                                    {
                                        switch (axis)
                                        {
                                            case 0: bone.rotationX = frame; break;
                                            case 1: bone.rotationY = frame; break;
                                            case 2: bone.rotationZ = frame; break;
                                        }
                                    }
                                    else
                                    {
                                        switch (axis)
                                        {
                                            case 0: bone.translationX = frame; break;
                                            case 1: bone.translationY = frame; break;
                                            case 2: bone.translationZ = frame; break;
                                        }
                                    }

                                    notExistMask <<= 1;
                                    constantMask <<= 1;
                                }

                                constantMask <<= 1;
                            }

                            break;
                        case RenderBase.OSegmentType.transformQuaternion:
                            bone.isFrameFormat = true;

                            long originalPos = data.Position;
                            uint rotationOffset = input.ReadUInt32();
                            uint translationOffset = input.ReadUInt32();

                            if ((flags & 0x10) == 0)
                            {
                                bone.rotationQuaternion.exists = true;
                                data.Seek(rotationOffset, SeekOrigin.Begin);

                                if ((flags & 2) > 0)
                                {
                                    bone.rotationQuaternion.vector.Add(new RenderBase.OVector4(
                                        input.ReadSingle(),
                                        input.ReadSingle(),
                                        input.ReadSingle(),
                                        input.ReadSingle()));
                                }
                                else
                                {
                                    bone.rotationQuaternion.startFrame = input.ReadSingle();
                                    bone.rotationQuaternion.endFrame = input.ReadSingle();

                                    uint rotationFlags = input.ReadUInt32();
                                    uint rotationDataOffset = input.ReadUInt32();
                                    uint rotationEntries = input.ReadUInt32();

                                    data.Seek(rotationDataOffset, SeekOrigin.Begin);
                                    for (int j = 0; j < rotationEntries; j++)
                                    {
                                        bone.rotationQuaternion.vector.Add(new RenderBase.OVector4(
                                            input.ReadSingle(),
                                            input.ReadSingle(),
                                            input.ReadSingle(),
                                            input.ReadSingle()));
                                    }
                                }
                            }

                            if ((flags & 8) == 0)
                            {
                                bone.translation.exists = true;
                                data.Seek(translationOffset, SeekOrigin.Begin);

                                if ((flags & 1) > 0)
                                {
                                    bone.translation.vector.Add(new RenderBase.OVector4(
                                        input.ReadSingle(),
                                        input.ReadSingle(),
                                        input.ReadSingle(),
                                        0));
                                }
                                else
                                {
                                    bone.translation.startFrame = input.ReadSingle();
                                    bone.translation.endFrame = input.ReadSingle();

                                    uint translationFlags = input.ReadUInt32();
                                    uint translationDataOffset = input.ReadUInt32();
                                    uint translationEntries = input.ReadUInt32();

                                    data.Seek(translationDataOffset, SeekOrigin.Begin);
                                    for (int j = 0; j < translationEntries; j++)
                                    {
                                        bone.translation.vector.Add(new RenderBase.OVector4(
                                            input.ReadSingle(),
                                            input.ReadSingle(),
                                            input.ReadSingle(),
                                            0));
                                    }
                                }
                            }

                            break;
                        case RenderBase.OSegmentType.transformMatrix:
                            bone.isFullBakedFormat = true;

                            input.ReadUInt32();
                            uint matrixOffset = input.ReadUInt32();
                            uint entries = input.ReadUInt32();

                            data.Seek(matrixOffset, SeekOrigin.Begin);
                            for (int j = 0; j < entries; j++)
                            {
                                RenderBase.OMatrix transform = new RenderBase.OMatrix();
                                transform.M11 = input.ReadSingle();
                                transform.M21 = input.ReadSingle();
                                transform.M31 = input.ReadSingle();
                                transform.M41 = input.ReadSingle();

                                transform.M12 = input.ReadSingle();
                                transform.M22 = input.ReadSingle();
                                transform.M32 = input.ReadSingle();
                                transform.M42 = input.ReadSingle();

                                transform.M13 = input.ReadSingle();
                                transform.M23 = input.ReadSingle();
                                transform.M33 = input.ReadSingle();
                                transform.M43 = input.ReadSingle();

                                bone.transform.Add(transform);
                            }

                            break;
                        default: throw new Exception(string.Format("BCH: Unknow Segment Type {0} on Skeletal Animation bone {1}! STOP!", segmentType, bone.name));
                    }

                    skeletalAnimation.bone.Add(bone);
                }

                models.skeletalAnimation.list.Add(skeletalAnimation);
            }

            //Material Animations
            for (int index = 0; index < contentHeader.materialAnimationsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.materialAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OMaterialAnimation materialAnimation = new RenderBase.OMaterialAnimation();

                materialAnimation.name = readString(input);
                uint animationFlags = input.ReadUInt32();
                materialAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
                materialAnimation.frameSize = input.ReadSingle();
                uint dataTableOffset = input.ReadUInt32();
                uint dataTableEntries = input.ReadUInt32();
                input.ReadUInt32();
                uint textureNameTableOffset = input.ReadUInt32();
                uint textureNameTableEntries = input.ReadUInt32();

                data.Seek(textureNameTableOffset, SeekOrigin.Begin);
                for (int i = 0; i < textureNameTableEntries; i++)
                {
                    string name = readString(input);
                    materialAnimation.textureName.Add(name);
                }

                for (int i = 0; i < dataTableEntries; i++)
                {
                    data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin);
                    uint offset = input.ReadUInt32();

                    RenderBase.OMaterialAnimationData animationData = new RenderBase.OMaterialAnimationData();

                    data.Seek(offset, SeekOrigin.Begin);
                    animationData.name = readString(input);
                    uint animationTypeFlags = input.ReadUInt32();
                    uint flags = input.ReadUInt32();

                    animationData.type = (RenderBase.OMaterialAnimationType)(animationTypeFlags & 0xff);
                    RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);

                    int segmentCount = 0;
                    switch (segmentType)
                    {
                        case RenderBase.OSegmentType.rgbaColor: segmentCount = 4; break;
                        case RenderBase.OSegmentType.vector2: segmentCount = 2; break;
                        case RenderBase.OSegmentType.single: segmentCount = 1; break;
                        case RenderBase.OSegmentType.integer: segmentCount = 1; break;
                    }

                    for (int j = 0; j < segmentCount; j++)
                    {
                        RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();

                        data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin);

                        frame.exists = (flags & (0x100 << j)) == 0;
                        bool constant = (flags & (1 << j)) > 0;

                        if (frame.exists)
                        {
                            if (constant)
                            {
                                frame.interpolation = RenderBase.OInterpolationMode.linear;
                                frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0));
                            }
                            else
                            {
                                uint frameOffset = input.ReadUInt32();
                                data.Seek(frameOffset, SeekOrigin.Begin);
                                getAnimationKeyFrame(input, frame);
                            }
                        }

                        animationData.frameList.Add(frame);
                    }

                    materialAnimation.data.Add(animationData);
                }

                models.materialAnimation.list.Add(materialAnimation);
            }

            //Visibility Animations
            for (int index = 0; index < contentHeader.visibilityAnimationsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.visibilityAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OVisibilityAnimation visibilityAnimation = new RenderBase.OVisibilityAnimation();

                visibilityAnimation.name = readString(input);
                uint animationFlags = input.ReadUInt32();
                visibilityAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
                visibilityAnimation.frameSize = input.ReadSingle();
                uint dataTableOffset = input.ReadUInt32();
                uint dataTableEntries = input.ReadUInt32();
                input.ReadUInt32();
                input.ReadUInt32();

                for (int i = 0; i < dataTableEntries; i++)
                {
                    data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin);
                    uint offset = input.ReadUInt32();

                    RenderBase.OVisibilityAnimationData animationData = new RenderBase.OVisibilityAnimationData();

                    data.Seek(offset, SeekOrigin.Begin);
                    animationData.name = readString(input);
                    uint animationTypeFlags = input.ReadUInt32();
                    uint flags = input.ReadUInt32();

                    RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);
                    if (segmentType == RenderBase.OSegmentType.boolean)
                    {
                        RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();
                        if (segmentType == RenderBase.OSegmentType.boolean) frame = getAnimationKeyFrameBool(input);
                        animationData.visibilityList = frame;
                    }

                    visibilityAnimation.data.Add(animationData);
                }

                models.visibilityAnimation.list.Add(visibilityAnimation);
            }

            //Light Animations
            for (int index = 0; index < contentHeader.lightAnimationsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.lightAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OLightAnimation lightAnimation = new RenderBase.OLightAnimation();

                lightAnimation.name = readString(input);
                uint animationFlags = input.ReadUInt32();
                lightAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
                lightAnimation.frameSize = input.ReadSingle();
                uint dataTableOffset = input.ReadUInt32();
                uint dataTableEntries = input.ReadUInt32();
                input.ReadUInt32();
                uint typeFlags = input.ReadUInt32();
                lightAnimation.lightType = (RenderBase.OLightType)((typeFlags & 3) - 1);
                lightAnimation.lightUse = (RenderBase.OLightUse)((typeFlags >> 2) & 3);

                for (int i = 0; i < dataTableEntries; i++)
                {
                    data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin);
                    uint offset = input.ReadUInt32();

                    RenderBase.OLightAnimationData animationData = new RenderBase.OLightAnimationData();

                    data.Seek(offset, SeekOrigin.Begin);
                    animationData.name = readString(input);
                    uint animationTypeFlags = input.ReadUInt32();
                    uint flags = input.ReadUInt32();

                    animationData.type = (RenderBase.OLightAnimationType)(animationTypeFlags & 0xff);
                    RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);

                    int segmentCount = 0;
                    switch (segmentType)
                    {
                        case RenderBase.OSegmentType.transform: segmentCount = 9; break;
                        case RenderBase.OSegmentType.rgbaColor: segmentCount = 4; break;
                        case RenderBase.OSegmentType.vector3: segmentCount = 3; break;
                        case RenderBase.OSegmentType.single: segmentCount = 1; break;
                        case RenderBase.OSegmentType.boolean: segmentCount = 1; break;
                    }

                    uint constantMask = 0x40;
                    for (int j = 0; j < segmentCount; j++)
                    {
                        RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();

                        data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin);

                        frame.exists = ((flags >> (segmentType == RenderBase.OSegmentType.transform ? 16 : 8)) & (1 << j)) == 0;

                        if (frame.exists)
                        {
                            if (segmentType == RenderBase.OSegmentType.boolean)
                            {
                                frame = getAnimationKeyFrameBool(input);
                            }
                            else
                            {
                                bool constant;
                                if (segmentType == RenderBase.OSegmentType.transform)
                                {
                                    constant = (flags & constantMask) > 0;
                                    if (j == 5)
                                        constantMask <<= 2;
                                    else
                                        constantMask <<= 1;
                                }
                                else
                                    constant = (flags & (1 << j)) > 0;

                                if (constant)
                                {
                                    frame.interpolation = RenderBase.OInterpolationMode.linear;
                                    frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0.0f));
                                }
                                else
                                {
                                    uint frameOffset = input.ReadUInt32();
                                    data.Seek(frameOffset, SeekOrigin.Begin);
                                    getAnimationKeyFrame(input, frame);
                                }
                            }
                        }

                        animationData.frameList.Add(frame);
                    }

                    lightAnimation.data.Add(animationData);
                }

                models.lightAnimation.list.Add(lightAnimation);
            }

            //Camera Animations
            for (int index = 0; index < contentHeader.cameraAnimationsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.cameraAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OCameraAnimation cameraAnimation = new RenderBase.OCameraAnimation();

                cameraAnimation.name = readString(input);
                uint animationFlags = input.ReadUInt32();
                cameraAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
                cameraAnimation.frameSize = input.ReadSingle();
                uint dataTableOffset = input.ReadUInt32();
                uint dataTableEntries = input.ReadUInt32();
                input.ReadUInt32();
                uint modeFlags = input.ReadUInt32();
                cameraAnimation.viewMode = (RenderBase.OCameraView)(modeFlags & 0xf);
                cameraAnimation.projectionMode = (RenderBase.OCameraProjection)((modeFlags >> 8) & 0xf);

                for (int i = 0; i < dataTableEntries; i++)
                {
                    data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin);
                    uint offset = input.ReadUInt32();

                    RenderBase.OCameraAnimationData animationData = new RenderBase.OCameraAnimationData();

                    data.Seek(offset, SeekOrigin.Begin);
                    animationData.name = readString(input);
                    uint animationTypeFlags = input.ReadUInt32();
                    uint flags = input.ReadUInt32();

                    animationData.type = (RenderBase.OCameraAnimationType)(animationTypeFlags & 0xff);
                    RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);

                    int segmentCount = 0;
                    switch (segmentType)
                    {
                        case RenderBase.OSegmentType.transform: segmentCount = 9; break;
                        case RenderBase.OSegmentType.vector3: segmentCount = 3; break;
                        case RenderBase.OSegmentType.single: segmentCount = 1; break;
                    }

                    uint constantMask = 0x40;
                    for (int j = 0; j < segmentCount; j++)
                    {
                        RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();

                        data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin);

                        frame.exists = ((flags >> (segmentType == RenderBase.OSegmentType.transform ? 16 : 8)) & (1 << j)) == 0;
                        bool constant;
                        if (segmentType == RenderBase.OSegmentType.transform)
                        {
                            constant = (flags & constantMask) > 0;
                            if (j == 5)
                                constantMask <<= 2;
                            else
                                constantMask <<= 1;
                        }
                        else
                            constant = (flags & (1 << j)) > 0;

                        if (frame.exists)
                        {
                            if (constant)
                            {
                                frame.interpolation = RenderBase.OInterpolationMode.linear;
                                frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0.0f));
                            }
                            else
                            {
                                uint frameOffset = input.ReadUInt32();
                                data.Seek(frameOffset, SeekOrigin.Begin);
                                getAnimationKeyFrame(input, frame);
                            }
                        }

                        animationData.frameList.Add(frame);
                    }

                    cameraAnimation.data.Add(animationData);
                }

                models.cameraAnimation.list.Add(cameraAnimation);
            }

            //Fog Animations
            for (int index = 0; index < contentHeader.fogAnimationsPointerTableEntries; index++)
            {
                data.Seek(contentHeader.fogAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OFogAnimation fogAnimation = new RenderBase.OFogAnimation();

                fogAnimation.name = readString(input);
                uint animationFlags = input.ReadUInt32();
                fogAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
                fogAnimation.frameSize = input.ReadSingle();
                uint dataTableOffset = input.ReadUInt32();
                uint dataTableEntries = input.ReadUInt32();
                input.ReadUInt32();
                input.ReadUInt32();

                for (int i = 0; i < dataTableEntries; i++)
                {
                    data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin);
                    uint offset = input.ReadUInt32();

                    RenderBase.OFogAnimationData animationData = new RenderBase.OFogAnimationData();

                    data.Seek(offset, SeekOrigin.Begin);
                    animationData.name = readString(input);
                    uint animationTypeFlags = input.ReadUInt32();
                    uint flags = input.ReadUInt32();

                    RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);
                    int segmentCount = segmentType == RenderBase.OSegmentType.rgbaColor ? 4 : 0;

                    for (int j = 0; j < segmentCount; j++)
                    {
                        RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();

                        data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin);

                        frame.exists = ((flags >> 8) & (1 << j)) == 0;

                        if (frame.exists)
                        {
                            bool constant = (flags & (1 << j)) > 0;

                            if (constant)
                            {
                                frame.interpolation = RenderBase.OInterpolationMode.linear;
                                frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0.0f));
                            }
                            else
                            {
                                uint frameOffset = input.ReadUInt32();
                                data.Seek(frameOffset, SeekOrigin.Begin);
                                getAnimationKeyFrame(input, frame);
                            }
                        }

                        animationData.colorList.Add(frame);
                    }

                    fogAnimation.data.Add(animationData);
                }

                models.fogAnimation.list.Add(fogAnimation);
            }

            //Scene Environment
            for (int index = 0; index < contentHeader.scenePointerTableEntries; index++)
            {
                data.Seek(contentHeader.scenePointerTableOffset + (index * 4), SeekOrigin.Begin);
                uint dataOffset = input.ReadUInt32();
                data.Seek(dataOffset, SeekOrigin.Begin);

                RenderBase.OScene scene = new RenderBase.OScene();
                scene.name = readString(input);

                uint cameraReferenceOffset = input.ReadUInt32();
                uint cameraReferenceEntries = input.ReadUInt32();
                uint lightReferenceOffset = input.ReadUInt32();
                uint lightReferenceEntries = input.ReadUInt32();
                uint fogReferenceOffset = input.ReadUInt32();
                uint fogReferenceEntries = input.ReadUInt32();

                data.Seek(cameraReferenceOffset, SeekOrigin.Begin);
                for (int i = 0; i < cameraReferenceEntries; i++)
                {
                    RenderBase.OSceneReference reference = new RenderBase.OSceneReference();
                    reference.slotIndex = input.ReadUInt32();
                    reference.name = readString(input);
                    scene.cameras.Add(reference);
                }

                data.Seek(lightReferenceOffset, SeekOrigin.Begin);
                for (int i = 0; i < lightReferenceEntries; i++)
                {
                    RenderBase.OSceneReference reference = new RenderBase.OSceneReference();
                    reference.slotIndex = input.ReadUInt32();
                    reference.name = readString(input);
                    scene.lights.Add(reference);
                }

                data.Seek(fogReferenceOffset, SeekOrigin.Begin);
                for (int i = 0; i < fogReferenceEntries; i++)
                {
                    RenderBase.OSceneReference reference = new RenderBase.OSceneReference();
                    reference.slotIndex = input.ReadUInt32();
                    reference.name = readString(input);
                    scene.fogs.Add(reference);
                }
            }

            //Models
            for (int modelIndex = 0; modelIndex < contentHeader.modelsPointerTableEntries; modelIndex++)
            {
                RenderBase.OModel model = new RenderBase.OModel();

                data.Seek(contentHeader.modelsPointerTableOffset + (modelIndex * 4), SeekOrigin.Begin);
                uint objectsHeaderOffset = input.ReadUInt32();

                //Objects header
                data.Seek(objectsHeaderOffset, SeekOrigin.Begin);
                bchModelHeader modelHeader;
                modelHeader.flags = input.ReadByte();
                modelHeader.skeletonScalingType = input.ReadByte();
                modelHeader.silhouetteMaterialEntries = input.ReadUInt16();

                modelHeader.worldTransform = new RenderBase.OMatrix();
                modelHeader.worldTransform.M11 = input.ReadSingle();
                modelHeader.worldTransform.M21 = input.ReadSingle();
                modelHeader.worldTransform.M31 = input.ReadSingle();
                modelHeader.worldTransform.M41 = input.ReadSingle();

                modelHeader.worldTransform.M12 = input.ReadSingle();
                modelHeader.worldTransform.M22 = input.ReadSingle();
                modelHeader.worldTransform.M32 = input.ReadSingle();
                modelHeader.worldTransform.M42 = input.ReadSingle();

                modelHeader.worldTransform.M13 = input.ReadSingle();
                modelHeader.worldTransform.M23 = input.ReadSingle();
                modelHeader.worldTransform.M33 = input.ReadSingle();
                modelHeader.worldTransform.M43 = input.ReadSingle();

                modelHeader.materialsTableOffset = input.ReadUInt32();
                modelHeader.materialsTableEntries = input.ReadUInt32();
                modelHeader.materialsNameOffset = input.ReadUInt32();
                modelHeader.verticesTableOffset = input.ReadUInt32();
                modelHeader.verticesTableEntries = input.ReadUInt32();
                data.Seek(header.backwardCompatibility > 6 ? 0x28 : 0x20, SeekOrigin.Current);
                modelHeader.skeletonOffset = input.ReadUInt32();
                modelHeader.skeletonEntries = input.ReadUInt32();
                modelHeader.skeletonNameOffset = input.ReadUInt32();
                modelHeader.objectsNodeVisibilityOffset = input.ReadUInt32();
                modelHeader.objectsNodeCount = input.ReadUInt32();
                modelHeader.modelName = readString(input);
                modelHeader.objectsNodeNameEntries = input.ReadUInt32();
                modelHeader.objectsNodeNameOffset = input.ReadUInt32();
                input.ReadUInt32(); //0x0
                modelHeader.metaDataPointerOffset = input.ReadUInt32();

                model.transform = modelHeader.worldTransform;
                model.name = modelHeader.modelName;

                string[] objectName = new string[modelHeader.objectsNodeNameEntries];
                data.Seek(modelHeader.objectsNodeNameOffset, SeekOrigin.Begin);
                int rootReferenceBit = input.ReadInt32(); //Radix tree
                uint rootLeftNode = input.ReadUInt16();
                uint rootRightNode = input.ReadUInt16();
                uint rootNameOffset = input.ReadUInt32();
                for (int i = 0; i < modelHeader.objectsNodeNameEntries; i++)
                {
                    int referenceBit = input.ReadInt32();
                    ushort leftNode = input.ReadUInt16();
                    ushort rightNode = input.ReadUInt16();
                    objectName[i] = readString(input);
                }

                //Materials
                for (int index = 0; index < modelHeader.materialsTableEntries; index++)
                {
                    //Nota: As versões mais antigas tinham o Coordinator já no header do material.
                    //As versões mais recentes tem uma seção reservada só pra ele, por isso possuem tamanho do header menor.
                    if (header.backwardCompatibility < 0x21)
                        data.Seek(modelHeader.materialsTableOffset + (index * 0x58), SeekOrigin.Begin);
                    else
                        data.Seek(modelHeader.materialsTableOffset + (index * 0x2c), SeekOrigin.Begin);

                    RenderBase.OMaterial material = new RenderBase.OMaterial();

                    uint materialParametersOffset = input.ReadUInt32();
                    input.ReadUInt32(); //TODO
                    input.ReadUInt32();
                    input.ReadUInt32();
                    uint textureCommandsOffset = input.ReadUInt32();
                    uint textureCommandsWordCount = input.ReadUInt32();

                    uint materialMapperOffset = 0;
                    if (header.backwardCompatibility < 0x21)
                    {
                        materialMapperOffset = (uint)data.Position;
                        data.Seek(0x30, SeekOrigin.Current);
                    }
                    else
                        materialMapperOffset = input.ReadUInt32();

                    material.name0 = readString(input);
                    material.name1 = readString(input);
                    material.name2 = readString(input);
                    material.name = readString(input);

                    //Parameters
                    //Same pointer of Materials section. Why?
                    if (materialParametersOffset != 0)
                    {
                        data.Seek(materialParametersOffset, SeekOrigin.Begin);
                        uint hash = input.ReadUInt32();

                        ushort materialFlags = input.ReadUInt16();
                        material.isFragmentLightEnabled = (materialFlags & 1) > 0;
                        material.isVertexLightEnabled = (materialFlags & 2) > 0;
                        material.isHemiSphereLightEnabled = (materialFlags & 4) > 0;
                        material.isHemiSphereOcclusionEnabled = (materialFlags & 8) > 0;
                        material.isFogEnabled = (materialFlags & 0x10) > 0;
                        material.rasterization.isPolygonOffsetEnabled = (materialFlags & 0x20) > 0;

                        ushort fragmentFlags = input.ReadUInt16();
                        material.fragmentShader.bump.isBumpRenormalize = (fragmentFlags & 1) > 0;
                        material.fragmentShader.lighting.isClampHighLight = (fragmentFlags & 2) > 0;
                        material.fragmentShader.lighting.isDistribution0Enabled = (fragmentFlags & 4) > 0;
                        material.fragmentShader.lighting.isDistribution1Enabled = (fragmentFlags & 8) > 0;
                        material.fragmentShader.lighting.isGeometryFactor0Enabled = (fragmentFlags & 0x10) > 0;
                        material.fragmentShader.lighting.isGeometryFactor1Enabled = (fragmentFlags & 0x20) > 0;
                        material.fragmentShader.lighting.isReflectionEnabled = (fragmentFlags & 0x40) > 0;
                        RenderBase.OBlendMode blendMode = (RenderBase.OBlendMode)((fragmentFlags >> 10) & 3);

                        input.ReadUInt32();
                        for (int i = 0; i < 3; i++)
                        {
                            RenderBase.OTextureCoordinator coordinator;
                            uint projectionAndCamera = input.ReadUInt32();
                            coordinator.projection = (RenderBase.OTextureProjection)((projectionAndCamera >> 16) & 0xff);
                            coordinator.referenceCamera = projectionAndCamera >> 24;
                            coordinator.scaleU = input.ReadSingle();
                            coordinator.scaleV = input.ReadSingle();
                            coordinator.rotate = input.ReadSingle();
                            coordinator.translateU = input.ReadSingle();
                            coordinator.translateV = input.ReadSingle();

                            material.textureCoordinator[i] = coordinator;
                        }

                        material.lightSetIndex = input.ReadUInt16();
                        material.fogIndex = input.ReadUInt16();

                        material.materialColor.emission = MeshUtils.getColor(input);
                        material.materialColor.ambient = MeshUtils.getColor(input);
                        material.materialColor.diffuse = MeshUtils.getColor(input);
                        material.materialColor.specular0 = MeshUtils.getColor(input);
                        material.materialColor.specular1 = MeshUtils.getColor(input);
                        material.materialColor.constant0 = MeshUtils.getColor(input);
                        material.materialColor.constant1 = MeshUtils.getColor(input);
                        material.materialColor.constant2 = MeshUtils.getColor(input);
                        material.materialColor.constant3 = MeshUtils.getColor(input);
                        material.materialColor.constant4 = MeshUtils.getColor(input);
                        material.materialColor.constant5 = MeshUtils.getColor(input);
                        material.fragmentOperation.blend.blendColor = MeshUtils.getColor(input);
                        material.materialColor.colorScale = input.ReadSingle();

                        input.ReadUInt32(); //TODO: Figure out
                        input.ReadUInt32();
                        input.ReadUInt32();
                        input.ReadUInt32();
                        input.ReadUInt32();
                        input.ReadUInt32();

                        uint fragmentData = input.ReadUInt32();
                        material.fragmentShader.bump.texture = (RenderBase.OBumpTexture)(fragmentData >> 24);
                        material.fragmentShader.bump.mode = (RenderBase.OBumpMode)((fragmentData >> 16) & 0xff);
                        material.fragmentShader.lighting.fresnelConfig = (RenderBase.OFresnelConfig)((fragmentData >> 8) & 0xff);
                        material.fragmentShader.layerConfig = fragmentData & 0xff;

                        //Some Fragment Lighting related commands... This seems a bit out of place.
                        long position = data.Position;
                        PICACommandReader fshLightingCommands = new PICACommandReader(data, 6, true);

                        PICACommand.fragmentSamplerAbsolute sAbs = fshLightingCommands.getReflectanceSamplerAbsolute();
                        material.fragmentShader.lighting.reflectanceRSampler.isAbsolute = sAbs.r;
                        material.fragmentShader.lighting.reflectanceGSampler.isAbsolute = sAbs.g;
                        material.fragmentShader.lighting.reflectanceBSampler.isAbsolute = sAbs.b;
                        material.fragmentShader.lighting.distribution0Sampler.isAbsolute = sAbs.d0;
                        material.fragmentShader.lighting.distribution1Sampler.isAbsolute = sAbs.d1;
                        material.fragmentShader.lighting.fresnelSampler.isAbsolute = sAbs.fresnel;

                        PICACommand.fragmentSamplerInput sInput = fshLightingCommands.getReflectanceSamplerInput();
                        material.fragmentShader.lighting.reflectanceRSampler.input = sInput.r;
                        material.fragmentShader.lighting.reflectanceGSampler.input = sInput.g;
                        material.fragmentShader.lighting.reflectanceBSampler.input = sInput.b;
                        material.fragmentShader.lighting.distribution0Sampler.input = sInput.d0;
                        material.fragmentShader.lighting.distribution1Sampler.input = sInput.d1;
                        material.fragmentShader.lighting.fresnelSampler.input = sInput.fresnel;

                        PICACommand.fragmentSamplerScale sScale = fshLightingCommands.getReflectanceSamplerScale();
                        material.fragmentShader.lighting.reflectanceRSampler.scale = sScale.r;
                        material.fragmentShader.lighting.reflectanceGSampler.scale = sScale.g;
                        material.fragmentShader.lighting.reflectanceBSampler.scale = sScale.b;
                        material.fragmentShader.lighting.distribution0Sampler.scale = sScale.d0;
                        material.fragmentShader.lighting.distribution1Sampler.scale = sScale.d1;
                        material.fragmentShader.lighting.fresnelSampler.scale = sScale.fresnel;
                        data.Seek(position + (4 * 6), SeekOrigin.Begin); //Just to be sure ;)

                        RenderBase.OConstantColor[] constantList = new RenderBase.OConstantColor[6];
                        uint constantColor = input.ReadUInt32();
                        for (int i = 0; i < 6; i++) constantList[i] = (RenderBase.OConstantColor)((constantColor >> (i * 4)) & 0xf);
                        material.rasterization.polygonOffsetUnit = input.ReadSingle();
                        uint fshCommandsOffset = input.ReadUInt32();
                        uint fshCommandsWordCount = input.ReadUInt32();
                        input.ReadUInt32();

                        material.fragmentShader.lighting.distribution0Sampler.tableName = readString(input);
                        material.fragmentShader.lighting.distribution1Sampler.tableName = readString(input);
                        material.fragmentShader.lighting.fresnelSampler.tableName = readString(input);
                        material.fragmentShader.lighting.reflectanceRSampler.tableName = readString(input);
                        material.fragmentShader.lighting.reflectanceGSampler.tableName = readString(input);
                        material.fragmentShader.lighting.reflectanceBSampler.tableName = readString(input);

                        material.fragmentShader.lighting.distribution0Sampler.samplerName = readString(input);
                        material.fragmentShader.lighting.distribution1Sampler.samplerName = readString(input);
                        material.fragmentShader.lighting.fresnelSampler.samplerName = readString(input);
                        material.fragmentShader.lighting.reflectanceRSampler.samplerName = readString(input);
                        material.fragmentShader.lighting.reflectanceGSampler.samplerName = readString(input);
                        material.fragmentShader.lighting.reflectanceBSampler.samplerName = readString(input);

                        material.shaderReference = new RenderBase.OReference(readString(input));
                        material.modelReference = new RenderBase.OReference(readString(input));

                        //User Data
                        if (header.backwardCompatibility > 6)
                        {
                            uint metaDataPointerOffset = input.ReadUInt32();
                            if (metaDataPointerOffset != 0)
                            {
                                data.Seek(metaDataPointerOffset, SeekOrigin.Begin);
                                material.userData = getMetaData(input);
                            }
                        }

                        //Mapper
                        data.Seek(materialMapperOffset, SeekOrigin.Begin);
                        for (int i = 0; i < 3; i++)
                        {
                            RenderBase.OTextureMapper mapper;
                            uint wrapAndMagFilter = input.ReadUInt32();
                            uint levelOfDetailAndMinFilter = input.ReadUInt32();
                            mapper.wrapU = (RenderBase.OTextureWrap)((wrapAndMagFilter >> 8) & 0xff);
                            mapper.wrapV = (RenderBase.OTextureWrap)((wrapAndMagFilter >> 16) & 0xff);
                            mapper.magFilter = (RenderBase.OTextureMagFilter)(wrapAndMagFilter >> 24);
                            mapper.minFilter = (RenderBase.OTextureMinFilter)(levelOfDetailAndMinFilter & 0xff);
                            mapper.minLOD = (levelOfDetailAndMinFilter >> 8) & 0xff; //max 232
                            mapper.LODBias = input.ReadSingle();
                            mapper.borderColor = MeshUtils.getColor(input);

                            material.textureMapper[i] = mapper;
                        }

                        //Fragment Shader commands
                        data.Seek(fshCommandsOffset, SeekOrigin.Begin);
                        PICACommandReader fshCommands = new PICACommandReader(data, fshCommandsWordCount);
                        for (byte stage = 0; stage < 6; stage++) material.fragmentShader.textureCombiner[stage] = fshCommands.getTevStage(stage);
                        material.fragmentShader.bufferColor = fshCommands.getFragmentBufferColor();
                        material.fragmentOperation.blend = fshCommands.getBlendOperation();
                        material.fragmentOperation.blend.logicalOperation = fshCommands.getColorLogicOperation();
                        material.fragmentShader.alphaTest = fshCommands.getAlphaTest();
                        material.fragmentOperation.stencil = fshCommands.getStencilTest();
                        material.fragmentOperation.depth = fshCommands.getDepthTest();
                        material.rasterization.cullMode = fshCommands.getCullMode();
                        material.fragmentOperation.blend.mode = blendMode;
                    }

                    model.material.Add(material);
                }

                //Skeleton
                data.Seek(modelHeader.skeletonOffset, SeekOrigin.Begin);
                for (int index = 0; index < modelHeader.skeletonEntries; index++)
                {
                    RenderBase.OBone bone = new RenderBase.OBone();

                    uint boneFlags = input.ReadUInt32();
                    bone.billboardMode = (RenderBase.OBillboardMode)((boneFlags >> 16) & 0xf);
                    bone.isSegmentScaleCompensate = (boneFlags & 0x00400000) > 0;
                    bone.parentId = input.ReadInt16();
                    ushort boneSpacer = input.ReadUInt16();
                    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);

                    RenderBase.OMatrix boneMatrix = new RenderBase.OMatrix();
                    boneMatrix.M11 = input.ReadSingle();
                    boneMatrix.M21 = input.ReadSingle();
                    boneMatrix.M31 = input.ReadSingle();
                    boneMatrix.M41 = input.ReadSingle();

                    boneMatrix.M12 = input.ReadSingle();
                    boneMatrix.M22 = input.ReadSingle();
                    boneMatrix.M32 = input.ReadSingle();
                    boneMatrix.M42 = input.ReadSingle();

                    boneMatrix.M13 = input.ReadSingle();
                    boneMatrix.M23 = input.ReadSingle();
                    boneMatrix.M33 = input.ReadSingle();
                    boneMatrix.M43 = input.ReadSingle();

                    bone.name = readString(input);

                    uint metaDataPointerOffset = input.ReadUInt32();
                    if (metaDataPointerOffset != 0)
                    {
                        long position = data.Position;
                        data.Seek(metaDataPointerOffset, SeekOrigin.Begin);
                        bone.userData = getMetaData(input);
                        data.Seek(position, SeekOrigin.Begin);
                    }

                    model.skeleton.Add(bone);
                }

                List<RenderBase.OMatrix> skeletonTransform = new List<RenderBase.OMatrix>();
                for (int index = 0; index < modelHeader.skeletonEntries; index++)
                {
                    RenderBase.OMatrix transform = new RenderBase.OMatrix();
                    transformSkeleton(model.skeleton, index, ref transform);
                    skeletonTransform.Add(transform);
                }

                data.Seek(modelHeader.objectsNodeVisibilityOffset, SeekOrigin.Begin);
                uint nodeVisibility = input.ReadUInt32();

                //Vertices header
                data.Seek(modelHeader.verticesTableOffset, SeekOrigin.Begin);
                List<bchObjectEntry> objects = new List<bchObjectEntry>();

                for (int index = 0; index < modelHeader.verticesTableEntries; index++)
                {
                    bchObjectEntry objectEntry = new bchObjectEntry();
                    objectEntry.materialId = input.ReadUInt16();
                    ushort flags = input.ReadUInt16();

                    if (header.backwardCompatibility != 8) objectEntry.isSilhouette = (flags & 1) > 0;
                    objectEntry.nodeId = input.ReadUInt16();
                    objectEntry.renderPriority = input.ReadUInt16();
                    objectEntry.vshAttributesBufferCommandsOffset = input.ReadUInt32(); //Buffer 0
                    objectEntry.vshAttributesBufferCommandsWordCount = input.ReadUInt32();
                    objectEntry.facesHeaderOffset = input.ReadUInt32();
                    objectEntry.facesHeaderEntries = input.ReadUInt32();
                    objectEntry.vshExtraAttributesBufferCommandsOffset = input.ReadUInt32(); //Buffers 1-11
                    objectEntry.vshExtraAttributesBufferCommandsWordCount = input.ReadUInt32();
                    objectEntry.centerVector = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                    objectEntry.flagsOffset = input.ReadUInt32();
                    input.ReadUInt32(); //ex: 0x0 fixo
                    objectEntry.boundingBoxOffset = input.ReadUInt32();

                    objects.Add(objectEntry);
                }

                for (int objIndex = 0; objIndex < objects.Count; objIndex++)
                {
                    if (objects[objIndex].isSilhouette) continue; //TODO: Figure out for what "Silhouette" is used.

                    RenderBase.OMesh obj = new RenderBase.OMesh();
                    obj.materialId = objects[objIndex].materialId;
                    obj.renderPriority = objects[objIndex].renderPriority;
                    if (objects[objIndex].nodeId < objectName.Length) obj.name = objectName[objects[objIndex].nodeId]; else obj.name = "mesh" + objIndex.ToString();
                    obj.isVisible = (nodeVisibility & (1 << objects[objIndex].nodeId)) > 0;

                    //Vertices
                    data.Seek(objects[objIndex].vshAttributesBufferCommandsOffset, SeekOrigin.Begin);
                    PICACommandReader vshCommands = new PICACommandReader(data, objects[objIndex].vshAttributesBufferCommandsWordCount);

                    Stack<float> vshAttributesUniformReg6 = vshCommands.getVSHFloatUniformData(6);
                    Stack<float> vshAttributesUniformReg7 = vshCommands.getVSHFloatUniformData(7);
                    RenderBase.OVector4 positionOffset = new RenderBase.OVector4(
                        vshAttributesUniformReg6.Pop(),
                        vshAttributesUniformReg6.Pop(),
                        vshAttributesUniformReg6.Pop(),
                        vshAttributesUniformReg6.Pop());
                    float texture0Scale = vshAttributesUniformReg7.Pop();
                    float texture1Scale = vshAttributesUniformReg7.Pop();
                    float texture2Scale = vshAttributesUniformReg7.Pop();
                    float boneWeightScale = vshAttributesUniformReg7.Pop();
                    float positionScale = vshAttributesUniformReg7.Pop();
                    float normalScale = vshAttributesUniformReg7.Pop();
                    float tangentScale = vshAttributesUniformReg7.Pop();
                    float colorScale = vshAttributesUniformReg7.Pop();

                    //Faces
                    uint facesCount = objects[objIndex].facesHeaderEntries;
                    bool hasFaces = facesCount > 0;
                    uint facesTableOffset = 0;
                    if (!hasFaces)
                    {
                        data.Seek(modelHeader.verticesTableOffset + modelHeader.verticesTableEntries * 0x38, SeekOrigin.Begin);
                        data.Seek(objIndex * 0x1c + 0x10, SeekOrigin.Current);

                        facesTableOffset = input.ReadUInt32();
                        facesCount = input.ReadUInt32();
                    }

                    for (uint f = 0; f < facesCount; f++)
                    {
                        RenderBase.OSkinningMode skinningMode = RenderBase.OSkinningMode.none;
                        List<ushort> nodeList = new List<ushort>();
                        uint idxBufferOffset;
                        PICACommand.indexBufferFormat idxBufferFormat;
                        uint idxBufferTotalVertices;

                        if (hasFaces)
                        {
                            uint baseOffset = objects[objIndex].facesHeaderOffset + f * 0x34;
                            data.Seek(baseOffset, SeekOrigin.Begin);
                            skinningMode = (RenderBase.OSkinningMode)input.ReadUInt16();
                            ushort nodeIdEntries = input.ReadUInt16();
                            for (int n = 0; n < nodeIdEntries; n++) nodeList.Add(input.ReadUInt16());

                            data.Seek(baseOffset + 0x2c, SeekOrigin.Begin);
                            uint faceHeaderOffset = input.ReadUInt32();
                            uint faceHeaderWordCount = input.ReadUInt32();
                            data.Seek(faceHeaderOffset, SeekOrigin.Begin);
                            PICACommandReader idxCommands = new PICACommandReader(data, faceHeaderWordCount);
                            idxBufferOffset = idxCommands.getIndexBufferAddress();
                            idxBufferFormat = idxCommands.getIndexBufferFormat();
                            idxBufferTotalVertices = idxCommands.getIndexBufferTotalVertices();
                        }
                        else
                        {
                            data.Seek(facesTableOffset + f * 8, SeekOrigin.Begin);
                            
                            idxBufferOffset = input.ReadUInt32();
                            idxBufferFormat = PICACommand.indexBufferFormat.unsignedShort;
                            idxBufferTotalVertices = input.ReadUInt32();
                        }

                        //Carregamento de dados relacionados ao Vertex Shader
                        uint vshAttributesBufferOffset = vshCommands.getVSHAttributesBufferAddress(0);
                        uint vshAttributesBufferStride = vshCommands.getVSHAttributesBufferStride(0);
                        uint vshTotalAttributes = vshCommands.getVSHTotalAttributes(0);
                        PICACommand.vshAttribute[] vshMainAttributesBufferPermutation = vshCommands.getVSHAttributesBufferPermutation();
                        uint[] vshAttributesBufferPermutation = vshCommands.getVSHAttributesBufferPermutation(0);
                        PICACommand.attributeFormat[] vshAttributesBufferFormat = vshCommands.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;
                            }
                        }

                        if (nodeList.Count > 0)
                        {
                            obj.hasNode = true;
                            obj.hasWeight = true;
                        }

                        data.Seek(idxBufferOffset, SeekOrigin.Begin);
                        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 = vshAttributesBufferOffset + (index * vshAttributesBufferStride);
                            data.Seek(vertexOffset, SeekOrigin.Begin);

                            RenderBase.OVertex vertex = new RenderBase.OVertex();
                            vertex.diffuseColor = 0xffffffff;
                            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:
                                        float x = (vector.x * positionScale) + positionOffset.x;
                                        float y = (vector.y * positionScale) + positionOffset.y;
                                        float z = (vector.z * positionScale) + positionOffset.z;
                                        vertex.position = new RenderBase.OVector3(x, y, z);
                                        break;
                                    case PICACommand.vshAttribute.normal:
                                        vertex.normal = new RenderBase.OVector3(vector.x * normalScale, vector.y * normalScale, vector.z * normalScale);
                                        break;
                                    case PICACommand.vshAttribute.tangent:
                                        vertex.tangent = new RenderBase.OVector3(vector.x * tangentScale, vector.y * tangentScale, vector.z * tangentScale);
                                        break;
                                    case PICACommand.vshAttribute.color:
                                        uint r = MeshUtils.saturate((vector.x * colorScale) * 0xff);
                                        uint g = MeshUtils.saturate((vector.y * colorScale) * 0xff);
                                        uint b = MeshUtils.saturate((vector.z * colorScale) * 0xff);
                                        uint a = MeshUtils.saturate((vector.w * colorScale) * 0xff);
                                        vertex.diffuseColor = b | (g << 8) | (r << 16) | (a << 24);
                                        break;
                                    case PICACommand.vshAttribute.textureCoordinate0:
                                        vertex.texture0 = new RenderBase.OVector2(vector.x * texture0Scale, vector.y * texture0Scale);
                                        break;
                                    case PICACommand.vshAttribute.textureCoordinate1:
                                        vertex.texture1 = new RenderBase.OVector2(vector.x * texture1Scale, vector.y * texture1Scale);
                                        break;
                                    case PICACommand.vshAttribute.textureCoordinate2:
                                        vertex.texture2 = new RenderBase.OVector2(vector.x * texture2Scale, vector.y * texture2Scale);
                                        break;
                                    case PICACommand.vshAttribute.boneIndex:
                                        vertex.node.Add(nodeList[(int)vector.x]);
                                        if (skinningMode == RenderBase.OSkinningMode.smoothSkinning)
                                        {
                                            if (format.attributeLength > 0) vertex.node.Add(nodeList[(int)vector.y]);
                                            if (format.attributeLength > 1) vertex.node.Add(nodeList[(int)vector.z]);
                                            if (format.attributeLength > 2) vertex.node.Add(nodeList[(int)vector.w]);
                                        }
                                        break;
                                    case PICACommand.vshAttribute.boneWeight:
                                        vertex.weight.Add(vector.x * boneWeightScale);
                                        if (skinningMode == RenderBase.OSkinningMode.smoothSkinning)
                                        {
                                            if (format.attributeLength > 0) vertex.weight.Add(vector.y * boneWeightScale);
                                            if (format.attributeLength > 1) vertex.weight.Add(vector.z * boneWeightScale);
                                            if (format.attributeLength > 2) vertex.weight.Add(vector.w * boneWeightScale);
                                        }
                                        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.Count <= 4)
                            {
                                for (int n = 0; n < nodeList.Count; n++) vertex.node.Add(nodeList[n]);
                                if (vertex.weight.Count == 0) vertex.weight.Add(1);
                            }

                            if (skinningMode != RenderBase.OSkinningMode.smoothSkinning && vertex.node.Count > 0)
                            {
                                //Note: Rigid skinning can have only one bone per vertex
                                //Note2: Vertex with Rigid skinning seems to be always have meshes centered, so is necessary to make them follow the skeleton
                                if (vertex.weight.Count == 0) vertex.weight.Add(1);
                                vertex.position = RenderBase.OVector3.transform(vertex.position, skeletonTransform[vertex.node[0]]);
                            }

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

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

                    //Bounding box
                    if (objects[objIndex].boundingBoxOffset != 0)
                    {
                        data.Seek(objects[objIndex].boundingBoxOffset, SeekOrigin.Begin);
                        uint bBoxDataOffset = input.ReadUInt32();
                        uint bBoxEntries = input.ReadUInt32();
                        uint bBoxNameOffset = input.ReadUInt32();

                        for (int index = 0; index < bBoxEntries; index++)
                        {
                            data.Seek(bBoxDataOffset + (index * 0xc), SeekOrigin.Begin);

                            RenderBase.OOrientedBoundingBox bBox = new RenderBase.OOrientedBoundingBox();

                            bBox.name = readString(input);
                            uint flags = input.ReadUInt32();
                            uint dataOffset = input.ReadUInt32();

                            data.Seek(dataOffset, SeekOrigin.Begin);
                            bBox.centerPosition = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());

                            bBox.orientationMatrix.M11 = input.ReadSingle();
                            bBox.orientationMatrix.M21 = input.ReadSingle();
                            bBox.orientationMatrix.M31 = input.ReadSingle();

                            bBox.orientationMatrix.M12 = input.ReadSingle();
                            bBox.orientationMatrix.M22 = input.ReadSingle();
                            bBox.orientationMatrix.M32 = input.ReadSingle();

                            bBox.orientationMatrix.M13 = input.ReadSingle();
                            bBox.orientationMatrix.M23 = input.ReadSingle();
                            bBox.orientationMatrix.M33 = input.ReadSingle();

                            bBox.size = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());

                            obj.boundingBox.Add(bBox);
                        }
                    }
    
                    model.mesh.Add(obj);
                }

                if (modelHeader.metaDataPointerOffset != 0)
                {
                    data.Seek(modelHeader.metaDataPointerOffset, SeekOrigin.Begin);
                    model.userData = getMetaData(input);
                }

                for (int index = 0; index < modelHeader.skeletonEntries; index++)
                {
                    scaleSkeleton(model.skeleton, index, index);
                }

                models.model.Add(model);
            }

            data.Close();

            return models;
        }
Пример #3
0
        public override void Read(string filename)
        {
            bchHeader header = new bchHeader();
            FileData  f      = new FileData(filename);

            f.Endian = System.IO.Endianness.Little;

            f.skip(4);
            header.backwardCompatibility = f.readByte();
            header.forwardCompatibility  = f.readByte();
            header.version = f.readShort();

            header.mainHeaderOffset  = f.readInt();
            header.stringTableOffset = f.readInt();
            header.gpuCommandsOffset = f.readInt();
            header.dataOffset        = f.readInt();
            if (header.backwardCompatibility > 0x20)
            {
                header.dataExtendedOffset = f.readInt();
            }
            header.relocationTableOffset = f.readInt();

            header.mainHeaderLength  = f.readInt();
            header.stringTableLength = f.readInt();
            header.gpuCommandsLength = f.readInt();
            header.dataLength        = f.readInt();
            if (header.backwardCompatibility > 0x20)
            {
                header.dataExtendedLength = f.readInt();
            }
            header.relocationTableLength = f.readInt();

            header.uninitializedDataSectionLength        = f.readInt();
            header.uninitializedDescriptionSectionLength = f.readInt();

            if (header.backwardCompatibility > 7)
            {
                header.flags        = f.readShort();
                header.addressCount = f.readShort();
            }

            // Relocation table
            for (int i = 0; i < header.relocationTableLength; i += 4)
            {
                f.seek(header.relocationTableOffset + i);
                int  val  = f.readInt();
                int  off  = val & 0x1FFFFFF;
                byte flag = (byte)(val >> 25);

                switch (flag)
                {
                case 0:
                    f.seek((off * 4) + header.mainHeaderOffset);
                    f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.mainHeaderOffset);
                    break;

                case 1:
                    f.seek(off + header.mainHeaderOffset);
                    f.writeInt((off) + header.mainHeaderOffset, f.readInt() + header.stringTableOffset);
                    break;

                case 2:
                    f.seek((off * 4) + header.mainHeaderOffset);
                    f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.gpuCommandsOffset);
                    break;

                case 0xc:
                    f.seek((off * 4) + header.mainHeaderOffset);
                    f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.dataOffset);
                    break;
                }

                f.seek((off * 4) + header.gpuCommandsOffset);
                if (header.backwardCompatibility < 6)
                {
                    switch (flag)
                    {
                    case 0x23: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Texture

                    case 0x25: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Vertex

                    //case 0x26: f.writeInt((off * 4) + header.gpuCommandsOffset, ((f.readInt() + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                    case 0x27: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break;     //Index 8 bits mode
                    }
                }
                else if (header.backwardCompatibility < 8)
                {
                    switch (flag)
                    {
                    case 0x24: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Texture

                    case 0x26: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Vertex

                    //case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                    case 0x28: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break;     //Index 8 bits mode
                    }
                }
                else if (header.backwardCompatibility < 0x21)
                {
                    switch (flag)
                    {
                    case 0x25: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Texture

                    case 0x27: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Vertex

                    //case 0x28: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                    case 0x29: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break;     //Index 8 bits mode
                    }
                }
                else
                {
                    switch (flag)
                    {
                    case 0x25: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Texture

                    case 0x26: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataOffset); break;     //Vertex relative to Data Offset

                    //case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Offset
                    case 0x28: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Offset

                    case 0x2b: f.writeInt((off * 4) + header.gpuCommandsOffset, f.readInt() + header.dataExtendedOffset); break;        //Vertex relative to Data Extended Offset

                    //case 0x2c: writer.Write(((peek(input) + header.dataExtendedOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Extended Offset
                    case 0x2d: f.writeInt((off * 4) + header.gpuCommandsOffset, (f.readInt() + header.dataExtendedOffset) & 0x7fffffff); break;     //Index 8 bits mode relative to Data Extended Offset
                    }
                }
            }


            // Content Header
            f.seek(header.mainHeaderOffset);
            bchContentHeader content = new bchContentHeader();

            {
                content.modelsPointerTableOffset        = f.readInt();
                content.modelsPointerTableEntries       = f.readInt();
                content.modelsNameOffset                = f.readInt();
                content.materialsPointerTableOffset     = f.readInt();
                content.materialsPointerTableEntries    = f.readInt();
                content.materialsNameOffset             = f.readInt();
                content.shadersPointerTableOffset       = f.readInt();
                content.shadersPointerTableEntries      = f.readInt();
                content.shadersNameOffset               = f.readInt();
                content.texturesPointerTableOffset      = f.readInt();
                content.texturesPointerTableEntries     = f.readInt();
                content.texturesNameOffset              = f.readInt();
                content.materialsLUTPointerTableOffset  = f.readInt();
                content.materialsLUTPointerTableEntries = f.readInt();
                content.materialsLUTNameOffset          = f.readInt();
                content.lightsPointerTableOffset        = f.readInt();
                content.lightsPointerTableEntries       = f.readInt();
                content.lightsNameOffset                = f.readInt();
                content.camerasPointerTableOffset       = f.readInt();
                content.camerasPointerTableEntries      = f.readInt();
                content.camerasNameOffset               = f.readInt();
                content.fogsPointerTableOffset          = f.readInt();
                content.fogsPointerTableEntries         = f.readInt();
                content.fogsNameOffset = f.readInt();
                content.skeletalAnimationsPointerTableOffset    = f.readInt();
                content.skeletalAnimationsPointerTableEntries   = f.readInt();
                content.skeletalAnimationsNameOffset            = f.readInt();
                content.materialAnimationsPointerTableOffset    = f.readInt();
                content.materialAnimationsPointerTableEntries   = f.readInt();
                content.materialAnimationsNameOffset            = f.readInt();
                content.visibilityAnimationsPointerTableOffset  = f.readInt();
                content.visibilityAnimationsPointerTableEntries = f.readInt();
                content.visibilityAnimationsNameOffset          = f.readInt();
                content.lightAnimationsPointerTableOffset       = f.readInt();
                content.lightAnimationsPointerTableEntries      = f.readInt();
                content.lightAnimationsNameOffset           = f.readInt();
                content.cameraAnimationsPointerTableOffset  = f.readInt();
                content.cameraAnimationsPointerTableEntries = f.readInt();
                content.cameraAnimationsNameOffset          = f.readInt();
                content.fogAnimationsPointerTableOffset     = f.readInt();
                content.fogAnimationsPointerTableEntries    = f.readInt();
                content.fogAnimationsNameOffset             = f.readInt();
                content.scenePointerTableOffset             = f.readInt();
                content.scenePointerTableEntries            = f.readInt();
                content.sceneNameOffset = f.readInt();
            }

            //Shaders (unused for now, until someone wants to add them)
            for (int index = 0; index < content.shadersPointerTableEntries; index++)
            {
                f.seek(content.shadersPointerTableOffset + (index * 4));
                int dataOffset = f.readInt();
                f.seek(dataOffset);

                int shaderDataOffset = f.readInt();
                int shaderDataLength = f.readInt();
            }

            // Textures
            // WIP Section
            for (int index = 0; index < content.texturesPointerTableEntries; index++)
            {
                f.seek(content.texturesPointerTableOffset + (index * 4));
                int dOffset = f.readInt();
                f.seek(dOffset);

                int textureCommandsOffset    = f.readInt();
                int textureCommandsWordCount = f.readInt();

                f.seek(f.pos() + 0x14);
                String textureName = f.readString(f.readInt(), -1);
                //Debug.WriteLine("gpuCommandOffset: " + header.gpuCommandsOffset.ToString("X"));
                f.seek(textureCommandsOffset);
                //Debug.WriteLine("textureCommandOffset: " + textureCommandsOffset.ToString("X"));
                BCH_Texture tex = new BCH_Texture();
                textures.Add(textureName, tex);

                tex.height = f.readShort();
                tex.width  = f.readShort();
                f.skip(12);
                int doffset = f.readInt();
                //Debug.WriteLine("doffset: " + doffset.ToString("X"));
                f.skip(4);
                tex.type = f.readInt();
                tex.data = f.getSection(doffset, f.size() - doffset);

                if (tex.type == 12)
                {
                    tex.display = NUT.loadImage(Pixel.decodeETC(tex.data, tex.width, tex.height));
                }
            }

            // Model data

            for (int modelIndex = 0; modelIndex < content.modelsPointerTableEntries; modelIndex++)
            {
                f.seek(content.modelsPointerTableOffset + (modelIndex * 4));
                int objectsHeaderOffset = f.readInt();

                // Objects
                f.seek(objectsHeaderOffset);
                BCH_Model model = new BCH_Model();
                models.Add(model);

                model.flags                     = f.readByte();
                model.skeletonScaleType         = f.readByte();
                model.silhouetteMaterialEntries = f.readShort();

                model.worldTransform = new Matrix4(f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat()
                                                   , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat()
                                                   , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat()
                                                   , 0, 0, 0, 1);

                model.materialsTableOffset  = f.readInt();
                model.materialsTableEntries = f.readInt();
                model.materialsNameOffset   = f.readInt();
                model.verticesTableOffset   = f.readInt();
                //Debug.WriteLine("Mesh Count: " + f.pos().ToString("X"));
                model.verticesTableEntries = f.readInt();
                f.skip(0x28);
                model.skeletonOffset              = f.readInt();
                model.skeletonEntries             = f.readInt();
                model.skeletonNameOffset          = f.readInt();
                model.objectsNodeVisibilityOffset = f.readInt();
                model.objectsNodeCount            = f.readInt();
                model.name = f.readString(f.readInt(), -1);
                model.objectsNodeNameEntries = f.readInt();
                model.objectsNodeNameOffset  = f.readInt();
                f.readInt(); //0x0
                model.metaDataPointerOffset = f.readInt();

                f.seek(model.objectsNodeVisibilityOffset);
                int nodeVisibility = f.readInt();

                string[] objectName = new string[model.objectsNodeNameEntries];
                f.seek(model.objectsNodeNameOffset);
                int rootReferenceBit = f.readInt(); //Radix tree
                int rootLeftNode     = f.readShort();
                int rootRightNode    = f.readShort();
                int rootNameOffset   = f.readInt() + header.mainHeaderOffset;

                for (int i = 0; i < model.objectsNodeNameEntries; i++)
                {
                    int   referenceBit = f.readInt();
                    short leftNode     = (short)f.readShort();
                    short rightNode    = (short)f.readShort();
                    objectName[i] = f.readString(f.readInt(), -1);
                    //Debug.WriteLine(objectName[i]);
                }

                // Materials
                // NOTE: MATERIALS AND OBJECT SECTIONS ARE REALLY MESSY ATM

                String[] materialNames = new String[model.materialsTableEntries];
                for (int index = 0; index < model.materialsTableEntries; index++)
                {
                    f.seek(model.materialsTableOffset + (index * 0x2c));

                    int materialParametersOffset = f.readInt();
                    f.readInt();
                    f.readInt();
                    f.readInt();
                    int textureCommandsOffset    = f.readInt();
                    int textureCommandsWordCount = f.readInt();

                    int materialMapperOffset = f.readInt();
                    materialNames[index] = f.readString(f.readInt(), -1);
                }

                // Object Descriptions...
                // Assumes MBN is already loaded for now
                f.seek(model.verticesTableOffset);
                List <objDes> objDescriptors = new List <objDes>();
                Debug.WriteLine(model.name);
                if (mbn == null)
                {
                    mbn = new Smash_Forge.MBN();
                    for (int index = 0; index < model.verticesTableEntries; index++)
                    {
                        mbn.mesh.Add(new MBN.Mesh());
                    }
                    mbn.PreRender();
                }
                for (int index = 0; index < mbn.mesh.Count; index++)
                {
                    int i = f.readShort();
                    if (index > mbn.mesh.Count)
                    {
                        break;
                    }
                    if (i > materialNames.Length)
                    {
                        break;
                    }
                    mbn.mesh[index].texId = textures[materialNames[i]].display;
                    Console.WriteLine("Tex index" + mbn.mesh[index].texId);
                    f.skip(2); // flags
                    int nameId = f.readShort();
                    mbn.mesh[index].name = objectName[nameId];

                    // node visibility TODO: finish...
                    //mbn.mesh[index].isVisible = ((nodeVisibility & (1 << nameId)) > 0);

                    mbn.mesh[index].renderPriority = f.readShort();

                    objDes des = new objDes();
                    objDescriptors.Add(des);
                    des.vshAttBufferCommandOffset = f.readInt();
                    des.vshAttBufferCommandCount  = f.readInt();
                    des.faceOffset = f.readInt();
                    des.faceCount  = f.readInt();
                    des.vshAttBufferCommandOffsetEx = f.readInt();
                    des.vshAttBufferCommandCountEx  = f.readInt();

                    f.skip(12);  // center vector
                    f.skip(4);   // flagsOffset
                    f.skip(4);   // 0?
                    f.readInt(); //bbOffsets[i] =  + mainheaderOffset

                    //Debug.WriteLine(des.vshAttBufferCommandOffset.ToString("X"));
                }


                //Skeleton
                f.seek(model.skeletonOffset);
                for (int index = 0; index < model.skeletonEntries; index++)
                {
                    Bone bone      = new Smash_Forge.Bone(model.skeleton);
                    int  boneFlags = f.readInt();
                    bone.parentIndex = (short)f.readShort();
                    short boneSpace = (short)f.readShort();
                    bone.scale       = new float[3];
                    bone.rotation    = new float[3];
                    bone.position    = new float[3];
                    bone.scale[0]    = f.readFloat();
                    bone.scale[1]    = f.readFloat();
                    bone.scale[2]    = f.readFloat();
                    bone.rotation[0] = f.readFloat();
                    bone.rotation[1] = f.readFloat();
                    bone.rotation[2] = f.readFloat();
                    bone.position[0] = f.readFloat();
                    bone.position[1] = f.readFloat();
                    bone.position[2] = f.readFloat();

                    // bone matrix... not really needed to be stored per say
                    f.skip(4 * 4 * 3);

                    bone.Text = f.readString(f.readInt(), -1);

                    f.skip(4); // Meta data

                    model.skeleton.bones.Add(bone);
                }
                model.skeleton.reset();
            }
        }
Пример #4
0
        public static AnimationGroupNode Read(string filename)
        {
            bchHeader header = new bchHeader();
            FileData  f      = new FileData(filename);

            f.Endian = System.IO.Endianness.Little;

            f.skip(4);
            header.backwardCompatibility = f.readByte();
            header.forwardCompatibility  = f.readByte();
            header.version = f.readUShort();

            header.mainHeaderOffset  = f.readInt();
            header.stringTableOffset = f.readInt();
            header.gpuCommandsOffset = f.readInt();
            header.dataOffset        = f.readInt();
            if (header.backwardCompatibility > 0x20)
            {
                header.dataExtendedOffset = f.readInt();
            }
            header.relocationTableOffset = f.readInt();

            header.mainHeaderLength  = f.readInt();
            header.stringTableLength = f.readInt();
            header.gpuCommandsLength = f.readInt();
            header.dataLength        = f.readInt();
            if (header.backwardCompatibility > 0x20)
            {
                header.dataExtendedLength = f.readInt();
            }
            header.relocationTableLength = f.readInt();

            header.uninitializedDataSectionLength        = f.readInt();
            header.uninitializedDescriptionSectionLength = f.readInt();

            if (header.backwardCompatibility > 7)
            {
                header.flags        = f.readUShort();
                header.addressCount = f.readUShort();
            }

            // Relocation table
            for (int i = 0; i < header.relocationTableLength; i += 4)
            {
                f.seek(header.relocationTableOffset + i);
                int  val  = f.readInt();
                int  off  = val & 0x1FFFFFF;
                byte flag = (byte)(val >> 25);

                switch (flag)
                {
                case 0:
                    f.seek((off * 4) + header.mainHeaderOffset);
                    f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.mainHeaderOffset);
                    break;

                case 1:
                    f.seek(off + header.mainHeaderOffset);
                    f.writeInt((off) + header.mainHeaderOffset, f.readInt() + header.stringTableOffset);
                    break;

                case 2:
                    f.seek((off * 4) + header.mainHeaderOffset);
                    f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.gpuCommandsOffset);
                    break;

                case 0xc:
                    f.seek((off * 4) + header.mainHeaderOffset);
                    f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.dataOffset);
                    break;
                }
            }


            // Content Header
            f.seek(header.mainHeaderOffset);
            bchContentHeader content = new bchContentHeader();
            {
                content.modelsPointerTableOffset        = f.readInt();
                content.modelsPointerTableEntries       = f.readInt();
                content.modelsNameOffset                = f.readInt();
                content.materialsPointerTableOffset     = f.readInt();
                content.materialsPointerTableEntries    = f.readInt();
                content.materialsNameOffset             = f.readInt();
                content.shadersPointerTableOffset       = f.readInt();
                content.shadersPointerTableEntries      = f.readInt();
                content.shadersNameOffset               = f.readInt();
                content.texturesPointerTableOffset      = f.readInt();
                content.texturesPointerTableEntries     = f.readInt();
                content.texturesNameOffset              = f.readInt();
                content.materialsLUTPointerTableOffset  = f.readInt();
                content.materialsLUTPointerTableEntries = f.readInt();
                content.materialsLUTNameOffset          = f.readInt();
                content.lightsPointerTableOffset        = f.readInt();
                content.lightsPointerTableEntries       = f.readInt();
                content.lightsNameOffset                = f.readInt();
                content.camerasPointerTableOffset       = f.readInt();
                content.camerasPointerTableEntries      = f.readInt();
                content.camerasNameOffset               = f.readInt();
                content.fogsPointerTableOffset          = f.readInt();
                content.fogsPointerTableEntries         = f.readInt();
                content.fogsNameOffset = f.readInt();
                content.skeletalAnimationsPointerTableOffset    = f.readInt();
                content.skeletalAnimationsPointerTableEntries   = f.readInt();
                content.skeletalAnimationsNameOffset            = f.readInt();
                content.materialAnimationsPointerTableOffset    = f.readInt();
                content.materialAnimationsPointerTableEntries   = f.readInt();
                content.materialAnimationsNameOffset            = f.readInt();
                content.visibilityAnimationsPointerTableOffset  = f.readInt();
                content.visibilityAnimationsPointerTableEntries = f.readInt();
                content.visibilityAnimationsNameOffset          = f.readInt();
                content.lightAnimationsPointerTableOffset       = f.readInt();
                content.lightAnimationsPointerTableEntries      = f.readInt();
                content.lightAnimationsNameOffset           = f.readInt();
                content.cameraAnimationsPointerTableOffset  = f.readInt();
                content.cameraAnimationsPointerTableEntries = f.readInt();
                content.cameraAnimationsNameOffset          = f.readInt();
                content.fogAnimationsPointerTableOffset     = f.readInt();
                content.fogAnimationsPointerTableEntries    = f.readInt();
                content.fogAnimationsNameOffset             = f.readInt();
                content.scenePointerTableOffset             = f.readInt();
                content.scenePointerTableEntries            = f.readInt();
                content.sceneNameOffset = f.readInt();
            }


            //Skeletal animation
            AnimationGroupNode ThisAnimation = new AnimationGroupNode()
            {
                Text = filename
            };

            for (int index1 = 0; index1 < content.skeletalAnimationsPointerTableEntries; index1++)//
            {
                f.seek(content.skeletalAnimationsPointerTableOffset + (index1 * 4));
                int dataOffset = f.readInt();
                f.seek(dataOffset);


                string skeletalAnimationName = f.readString(f.readInt(), -1);
                int    animationFlags        = f.readInt();
                //int skeletalAnimationloopMode = f.readByte();  //pas ça du tout
                float skeletalAnimationframeSize = f.readFloat();
                int   boneTableOffset            = f.readInt();
                int   boneTableEntries           = f.readInt();
                int   metaDataPointerOffset      = f.readInt();

                //Runtime.Animations.Add(skeletalAnimationName, a);
                //MainForm.animNode.Nodes.Add(skeletalAnimationName);

                //Debug.WriteLine("Animation Name: " + skeletalAnimationName);
                Animation a = new Animation(skeletalAnimationName);
                ThisAnimation.Nodes.Add(a);

                for (int i = 0; i < boneTableEntries; i++)
                {
                    f.seek(boneTableOffset + (i * 4));
                    int offset = f.readInt();

                    Animation.KeyNode bone = new Animation.KeyNode("");
                    a.Bones.Add(bone);
                    f.seek(offset);
                    bone.Text = f.readString(f.readInt(), -1);
                    //Console.WriteLine("Bone Name: " + bone.name);
                    int  animationTypeFlags = f.readInt();
                    uint flags = (uint)f.readInt();

                    OSegmentType segmentType = (OSegmentType)((animationTypeFlags >> 16) & 0xf);
                    //Debug.WriteLine(bone.Text + " " + flags.ToString("x"));
                    switch (segmentType)
                    {
                    case OSegmentType.transform:
                        f.seek(offset + 0xC);
                        //Console.WriteLine(f.pos().ToString("x") + " " + flags.ToString("x"));

                        uint notExistMask = 0x10000;
                        uint constantMask = 0x40;

                        for (int j = 0; j < 3; j++)
                        {
                            for (int axis = 0; axis < 3; axis++)
                            {
                                bool notExist = (flags & notExistMask) > 0;
                                bool constant = (flags & constantMask) > 0;
                                //Console.WriteLine(notExist + " " + constant);

                                Animation.KeyGroup group = new Animation.KeyGroup();
                                //frame.exists = !notExist;
                                if (!notExist)
                                {
                                    if (constant)
                                    {
                                        Animation.KeyFrame frame = new Animation.KeyFrame();
                                        frame.InterType = Animation.InterpolationType.LINEAR;
                                        frame.Value     = f.readFloat();
                                        frame.Frame     = 0;
                                        group.Keys.Add(frame);
                                    }
                                    else
                                    {
                                        int frameOffset = f.readInt();
                                        int position    = f.pos();
                                        f.seek(frameOffset);
                                        float c = 0;
                                        //Debug.WriteLine(j + " " + axis + " " + bone.Text);
                                        getAnimationKeyFrame(f, group, out c);
                                        if (c > a.FrameCount)
                                        {
                                            a.FrameCount = (int)c;
                                        }
                                        f.seek(position);
                                    }
                                }
                                else
                                {
                                    f.seek(f.pos() + 0x04);
                                }
                                bone.RotType = Animation.RotationType.EULER;

                                if (j == 0)
                                {
                                    switch (axis)
                                    {
                                    case 0: bone.XSCA = group; break;

                                    case 1: bone.YSCA = group; break;

                                    case 2: bone.ZSCA = group; break;
                                    }
                                }
                                else
                                if (j == 1)
                                {
                                    switch (axis)
                                    {
                                    case 0: bone.XROT = group; break;

                                    case 1: bone.YROT = group; break;

                                    case 2: bone.ZROT = group; break;
                                    }
                                }
                                else
                                if (j == 2)
                                {
                                    switch (axis)
                                    {
                                    case 0: bone.XPOS = group; break;

                                    case 1: bone.YPOS = group; break;

                                    case 2: bone.ZPOS = group; break;
                                    }
                                }

                                notExistMask <<= 1;
                                constantMask <<= 1;
                            }
                            if (j == 1)
                            {
                                constantMask <<= 1;
                            }
                        }

                        break;

                    /*case OSegmentType.transformQuaternion:
                     *  bone.isFrameFormat = true;
                     *
                     *  int scaleOffset = f.readInt();
                     *  int rotationOffset = f.readInt();
                     *  int translationOffset = f.readInt();
                     *
                     *  if ((flags & 0x20) == 0)
                     *  {
                     *      bone.scale.exists = true;
                     *      f.seek(scaleOffset);
                     *
                     *      if ((flags & 4) > 0)
                     *      {
                     *          bone.scale.vector.Add(new Vector4(
                     *              f.readFloat(),
                     *              f.readFloat(),
                     *              f.readFloat(),
                     *              0));
                     *      }
                     *      else
                     *      {
                     *          bone.scale.startFrame = f.readFloat();
                     *          bone.scale.endFrame = f.readFloat();
                     *
                     *          int scaleFlags = f.readInt();
                     *          int scaleDataOffset = f.readInt();
                     *          int scaleEntries = f.readInt();
                     *
                     *          f.seek(scaleDataOffset);
                     *          for (int j = 0; j < scaleEntries; j++)
                     *          {
                     *              bone.scale.vector.Add(new Vector4(
                     *                  f.readFloat(),
                     *                  f.readFloat(),
                     *                  f.readFloat(),
                     *                  0));
                     *          }
                     *      }
                     *  }
                     *
                     *  if ((flags & 0x10) == 0)
                     *  {
                     *      bone.rotationQuaternion.exists = true;
                     *      f.seek(rotationOffset);
                     *
                     *      if ((flags & 2) > 0)
                     *      {
                     *          bone.rotationQuaternion.vector.Add(new Vector4(
                     *              f.readFloat(),
                     *              f.readFloat(),
                     *              f.readFloat(),
                     *              f.readFloat()));
                     *      }
                     *      else
                     *      {
                     *          bone.rotationQuaternion.startFrame = f.readFloat();
                     *          bone.rotationQuaternion.endFrame = f.readFloat();
                     *
                     *          int rotationFlags = f.readInt();
                     *          int rotationDataOffset = f.readInt();
                     *          int rotationEntries = f.readInt();
                     *
                     *          f.seek(rotationDataOffset);
                     *          for (int j = 0; j < rotationEntries; j++)
                     *          {
                     *              bone.rotationQuaternion.vector.Add(new Vector4(
                     *                  f.readFloat(),
                     *                  f.readFloat(),
                     *                  f.readFloat(),
                     *                  f.readFloat()));
                     *          }
                     *      }
                     *  }
                     *
                     *  if ((flags & 8) == 0)
                     *  {
                     *      bone.translation.exists = true;
                     *      f.seek(translationOffset);
                     *
                     *      if ((flags & 1) > 0)
                     *      {
                     *          bone.translation.vector.Add(new Vector4(
                     *              f.readFloat(),
                     *              f.readFloat(),
                     *              f.readFloat(),
                     *              0));
                     *      }
                     *      else
                     *      {
                     *          bone.translation.startFrame = f.readFloat();
                     *          bone.translation.endFrame = f.readFloat();
                     *
                     *          int translationFlags = f.readInt();
                     *          int translationDataOffset = f.readInt();
                     *          int translationEntries = f.readInt();
                     *
                     *          f.seek(translationDataOffset);
                     *          for (int j = 0; j < translationEntries; j++)
                     *          {
                     *              bone.translation.vector.Add(new Vector4(
                     *                  f.readFloat(),
                     *                  f.readFloat(),
                     *                  f.readFloat(),
                     *                  0));
                     *          }
                     *      }
                     *  }
                     *
                     *  break;
                     * case OSegmentType.transformMatrix:
                     *  bone.isFullBakedFormat = true;
                     *
                     *  f.readInt();
                     *  f.readInt();
                     *  int matrixOffset = f.readInt();
                     *  int entries = f.readInt();
                     *
                     *  f.seek(matrixOffset);
                     *  for (int j = 0; j < entries; j++)
                     *  {
                     *      OMatrix transform = new OMatrix();
                     *      transform.M11 = f.readFloat();
                     *      transform.M21 = f.readFloat();
                     *      transform.M31 = f.readFloat();
                     *      transform.M41 = f.readFloat();
                     *
                     *      transform.M12 = f.readFloat();
                     *      transform.M22 = f.readFloat();
                     *      transform.M32 = f.readFloat();
                     *      transform.M42 = f.readFloat();
                     *
                     *      transform.M13 = f.readFloat();
                     *      transform.M23 = f.readFloat();
                     *      transform.M33 = f.readFloat();
                     *      transform.M43 = f.readFloat();
                     *
                     *      bone.transform.Add(transform);
                     *  }
                     *
                     *  break;*/
                    default: throw new Exception(string.Format("BCH: Unknow Segment Type {0} on Skeletal Animation bone {1}! STOP!", segmentType, bone.Text));
                    }

                    //skeletalAnimation.bone.Add(bone);
                }
            }
            //return a;
            return(ThisAnimation);
        }
Пример #5
0
        public override void Read(string filename)
        {
            bchHeader header = new bchHeader();
            FileData  f      = new FileData(filename);

            f.Endian = System.IO.Endianness.Little;

            f.skip(4);
            header.backwardCompatibility = f.readByte();
            header.forwardCompatibility  = f.readByte();
            header.version = f.readShort();

            header.mainHeaderOffset  = f.readInt();
            header.stringTableOffset = f.readInt();
            header.gpuCommandsOffset = f.readInt();
            header.dataOffset        = f.readInt();
            if (header.backwardCompatibility > 0x20)
            {
                header.dataExtendedOffset = f.readInt();
            }
            header.relocationTableOffset = f.readInt();

            header.mainHeaderLength  = f.readInt();
            header.stringTableLength = f.readInt();
            header.gpuCommandsLength = f.readInt();
            header.dataLength        = f.readInt();
            if (header.backwardCompatibility > 0x20)
            {
                header.dataExtendedLength = f.readInt();
            }
            header.relocationTableLength = f.readInt();

            header.uninitializedDataSectionLength        = f.readInt();
            header.uninitializedDescriptionSectionLength = f.readInt();

            if (header.backwardCompatibility > 7)
            {
                header.flags        = f.readShort();
                header.addressCount = f.readShort();
            }

            // TODO: Finished Relocation table stuff
            for (int i = 0; i < header.relocationTableLength; i += 4)
            {
                f.seek(header.relocationTableOffset + i);
                int  val  = f.readInt();
                int  off  = val & 0x1FFFFFF;
                byte flag = (byte)(val >> 25);

                switch (flag)
                {
                case 0:
                    f.seek((off * 4) + header.mainHeaderOffset);
                    f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.mainHeaderOffset);
                    break;

                case 1:
                    f.seek(off + header.mainHeaderOffset);
                    f.writeInt((off) + header.mainHeaderOffset, f.readInt() + header.stringTableOffset);
                    break;

                case 2:
                    f.seek((off * 4) + header.mainHeaderOffset);
                    f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.gpuCommandsOffset);
                    break;

                case 0xc:
                    f.seek((off * 4) + header.mainHeaderOffset);
                    f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.dataOffset);
                    break;
                }

                /*f.seek((off * 4) + header.gpuCommandsOffset);
                 * if (header.backwardCompatibility < 6)
                 * {
                 *  switch (flag)
                 *  {
                 *      case 0x23: f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.dataOffset); break; //Texture
                 *      case 0x25: f.writeInt((off * 4) + header.mainHeaderOffset, f.readInt() + header.dataOffset); break; //Vertex
                 *      // Trying to understand the mess first
                 *      case 0x26: f.writeInt((off * 4) + header.mainHeaderOffset, Convert.ToInt32(((f.readInt() + header.dataOffset) & 0x7fffffff) | 0x80000000)); break; //Index 16 bits mode
                 *                                                                                                                                                           //case 0x27: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode
                 *  }
                 * }
                 *              else if (header.backwardCompatibility < 8)
                 *              {
                 *                  switch (flag)
                 *                  {
                 *                      case 0x24: writer.Write(peek(input) + header.dataOffset); break; //Texture
                 *                      case 0x26: writer.Write(peek(input) + header.dataOffset); break; //Vertex
                 *                      case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                 *                      case 0x28: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode
                 *                  }
                 *              }
                 *              else if (header.backwardCompatibility < 0x21)
                 *              {
                 *                  switch (flag)
                 *                  {
                 *                      case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Texture
                 *                      case 0x27: writer.Write(peek(input) + header.dataOffset); break; //Vertex
                 *                      case 0x28: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode
                 *                      case 0x29: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode
                 *                  }
                 *              }
                 *              else
                 *              {
                 *                  switch (flag)
                 *                  {
                 *                      case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Texture
                 *                      case 0x26: writer.Write(peek(input) + header.dataOffset); break; //Vertex relative to Data Offset
                 *                      case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Offset
                 *                      case 0x28: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Offset
                 *                      case 0x2b: writer.Write(peek(input) + header.dataExtendedOffset); break; //Vertex relative to Data Extended Offset
                 *                      case 0x2c: writer.Write(((peek(input) + header.dataExtendedOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Extended Offset
                 *                      case 0x2d: writer.Write((peek(input) + header.dataExtendedOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Extended Offset
                 *                  }
                 *              }
                 */
            }


            // Content Header
            f.seek(header.mainHeaderOffset);
            bchContentHeader content = new bchContentHeader();

            {
                content.modelsPointerTableOffset        = f.readInt();
                content.modelsPointerTableEntries       = f.readInt();
                content.modelsNameOffset                = f.readInt();
                content.materialsPointerTableOffset     = f.readInt();
                content.materialsPointerTableEntries    = f.readInt();
                content.materialsNameOffset             = f.readInt();
                content.shadersPointerTableOffset       = f.readInt();
                content.shadersPointerTableEntries      = f.readInt();
                content.shadersNameOffset               = f.readInt();
                content.texturesPointerTableOffset      = f.readInt();
                content.texturesPointerTableEntries     = f.readInt();
                content.texturesNameOffset              = f.readInt();
                content.materialsLUTPointerTableOffset  = f.readInt();
                content.materialsLUTPointerTableEntries = f.readInt();
                content.materialsLUTNameOffset          = f.readInt();
                content.lightsPointerTableOffset        = f.readInt();
                content.lightsPointerTableEntries       = f.readInt();
                content.lightsNameOffset                = f.readInt();
                content.camerasPointerTableOffset       = f.readInt();
                content.camerasPointerTableEntries      = f.readInt();
                content.camerasNameOffset               = f.readInt();
                content.fogsPointerTableOffset          = f.readInt();
                content.fogsPointerTableEntries         = f.readInt();
                content.fogsNameOffset = f.readInt();
                content.skeletalAnimationsPointerTableOffset    = f.readInt();
                content.skeletalAnimationsPointerTableEntries   = f.readInt();
                content.skeletalAnimationsNameOffset            = f.readInt();
                content.materialAnimationsPointerTableOffset    = f.readInt();
                content.materialAnimationsPointerTableEntries   = f.readInt();
                content.materialAnimationsNameOffset            = f.readInt();
                content.visibilityAnimationsPointerTableOffset  = f.readInt();
                content.visibilityAnimationsPointerTableEntries = f.readInt();
                content.visibilityAnimationsNameOffset          = f.readInt();
                content.lightAnimationsPointerTableOffset       = f.readInt();
                content.lightAnimationsPointerTableEntries      = f.readInt();
                content.lightAnimationsNameOffset           = f.readInt();
                content.cameraAnimationsPointerTableOffset  = f.readInt();
                content.cameraAnimationsPointerTableEntries = f.readInt();
                content.cameraAnimationsNameOffset          = f.readInt();
                content.fogAnimationsPointerTableOffset     = f.readInt();
                content.fogAnimationsPointerTableEntries    = f.readInt();
                content.fogAnimationsNameOffset             = f.readInt();
                content.scenePointerTableOffset             = f.readInt();
                content.scenePointerTableEntries            = f.readInt();
                content.sceneNameOffset = f.readInt();
            }

            //Shaders (unused for now, until someone wants to add them)
            for (int index = 0; index < content.shadersPointerTableEntries; index++)
            {
                f.seek(content.shadersPointerTableOffset + (index * 4));
                int dataOffset = f.readInt();
                f.seek(dataOffset);

                int shaderDataOffset = f.readInt();
                int shaderDataLength = f.readInt();
            }

            // Textures
            // WIP Section
            for (int index = 0; index < content.texturesPointerTableEntries; index++)
            {
                f.seek(content.texturesPointerTableOffset + (index * 4));
                int dOffset = f.readInt();
                f.seek(dOffset);
                Debug.WriteLine("dOffset: " + dOffset.ToString("X"));
                int textureCommandsOffset    = f.readInt();
                int textureCommandsWordCount = f.readInt();

                f.seek(f.pos() + 0x14);
                String textureName = f.readString(f.readInt(), -1);
                //Debug.WriteLine("gpuCommandOffset: " + header.gpuCommandsOffset.ToString("X"));
                f.seek(textureCommandsOffset);
                //Debug.WriteLine("textureCommandOffset: " + textureCommandsOffset.ToString("X"));
                BCH_Texture tex = new BCH_Texture();
                textures.Add(textureName, tex);

                tex.height = f.readShort();
                tex.width  = f.readShort();
                f.skip(12);
                int doffset = f.readInt();
                //Debug.WriteLine("doffset: " + doffset.ToString("X"));
                f.skip(4);
                tex.type = f.readInt();
                tex.data = f.getSection(doffset, f.size() - doffset);

                if (tex.type == 12)
                {
                    tex.display = NUT.loadImage(Pixel.decodeETC(tex.data, tex.width, tex.height));
                }
            }

            // Model data

            for (int modelIndex = 0; modelIndex < content.modelsPointerTableEntries; modelIndex++)
            {
                f.seek(content.modelsPointerTableOffset + (modelIndex * 4));
                int objectsHeaderOffset = f.readInt();

                // Objects
                f.seek(objectsHeaderOffset);
                BCH_Model model = new BCH_Model();
                models.Add(model);

                model.flags                     = f.readByte();
                model.skeletonScaleType         = f.readByte();
                model.silhouetteMaterialEntries = f.readShort();

                model.worldTransform = new Matrix4(f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat()
                                                   , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat()
                                                   , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat()
                                                   , 0, 0, 0, 1);

                model.materialsTableOffset  = f.readInt();
                model.materialsTableEntries = f.readInt();
                model.materialsNameOffset   = f.readInt();
                model.verticesTableOffset   = f.readInt();
                //Debug.WriteLine("Mesh Count: " + f.pos().ToString("X"));
                model.verticesTableEntries = f.readInt();
                f.skip(0x28);
                model.skeletonOffset              = f.readInt();
                model.skeletonEntries             = f.readInt();
                model.skeletonNameOffset          = f.readInt();
                model.objectsNodeVisibilityOffset = f.readInt();
                model.objectsNodeCount            = f.readInt();
                model.name = f.readString(f.readInt(), -1);
                model.objectsNodeNameEntries = f.readInt();
                model.objectsNodeNameOffset  = f.readInt();
                f.readInt(); //0x0
                model.metaDataPointerOffset = f.readInt();

                f.seek(model.objectsNodeVisibilityOffset);
                int nodeVisibility = f.readInt();

                string[] objectName = new string[model.objectsNodeNameEntries];
                f.seek(model.objectsNodeNameOffset);
                int rootReferenceBit = f.readInt(); //Radix tree
                int rootLeftNode     = f.readShort();
                int rootRightNode    = f.readShort();
                int rootNameOffset   = f.readInt() + header.mainHeaderOffset;

                for (int i = 0; i < model.objectsNodeNameEntries; i++)
                {
                    int   referenceBit = f.readInt();
                    short leftNode     = (short)f.readShort();
                    short rightNode    = (short)f.readShort();
                    objectName[i] = f.readString(f.readInt(), -1);
                    //Debug.WriteLine(objectName[i]);
                }

                // Materials
                // NOTE: MATERIALS AND OBJECT SECTIONS ARE REALLY MESSY ATM

                String[] materialNames = new String[model.materialsTableEntries];
                for (int index = 0; index < model.materialsTableEntries; index++)
                {
                    f.seek(model.materialsTableOffset + (index * 0x2c));

                    int materialParametersOffset = f.readInt();
                    f.readInt();
                    f.readInt();
                    f.readInt();
                    int textureCommandsOffset    = f.readInt();
                    int textureCommandsWordCount = f.readInt();

                    int materialMapperOffset = f.readInt();
                    materialNames[index] = f.readString(f.readInt(), -1);
                }

                // Object Descriptions...
                // Assumes MBN is already loaded for now
                f.seek(model.verticesTableOffset);
                List <objDes> objDescriptors = new List <objDes>();
                Debug.WriteLine(model.name);
                if (mbn == null)
                {
                    mbn = new Smash_Forge.MBN();
                    for (int index = 0; index < model.verticesTableEntries; index++)
                    {
                        mbn.mesh.Add(new MBN.Mesh());
                    }
                    mbn.PreRender();
                }
                for (int index = 0; index < mbn.mesh.Count; index++)
                {
                    int i = f.readShort();
                    if (index > mbn.mesh.Count)
                    {
                        break;
                    }
                    if (i > materialNames.Length)
                    {
                        break;
                    }
                    mbn.mesh[index].texId = textures[materialNames[i]].display;
                    Console.WriteLine("Tex index" + mbn.mesh[index].texId);
                    f.skip(2); // flags
                    int nameId = f.readShort();
                    mbn.mesh[index].name = objectName[nameId];

                    // node visibility TODO: finish...
                    //mbn.mesh[index].isVisible = ((nodeVisibility & (1 << nameId)) > 0);

                    mbn.mesh[index].renderPriority = f.readShort();

                    objDes des = new objDes();
                    objDescriptors.Add(des);
                    des.vshAttBufferCommandOffset = f.readInt();
                    des.vshAttBufferCommandCount  = f.readInt();
                    des.faceOffset = f.readInt();
                    des.faceCount  = f.readInt();
                    des.vshAttBufferCommandOffsetEx = f.readInt();
                    des.vshAttBufferCommandCountEx  = f.readInt();

                    f.skip(12);  // center vector
                    f.skip(4);   // flagsOffset
                    f.skip(4);   // 0?
                    f.readInt(); //bbOffsets[i] =  + mainheaderOffset

                    //Debug.WriteLine(des.vshAttBufferCommandOffset.ToString("X"));
                }


                //Skeleton
                f.seek(model.skeletonOffset);
                for (int index = 0; index < model.skeletonEntries; index++)
                {
                    Bone bone      = new Smash_Forge.Bone(model.skeleton);
                    int  boneFlags = f.readInt();
                    bone.parentIndex = (short)f.readShort();
                    short boneSpace = (short)f.readShort();
                    bone.scale       = new float[3];
                    bone.rotation    = new float[3];
                    bone.position    = new float[3];
                    bone.scale[0]    = f.readFloat();
                    bone.scale[1]    = f.readFloat();
                    bone.scale[2]    = f.readFloat();
                    bone.rotation[0] = f.readFloat();
                    bone.rotation[1] = f.readFloat();
                    bone.rotation[2] = f.readFloat();
                    bone.position[0] = f.readFloat();
                    bone.position[1] = f.readFloat();
                    bone.position[2] = f.readFloat();

                    // bone matrix... not really needed to be stored per say
                    f.skip(4 * 4 * 3);

                    bone.boneName = f.readString(f.readInt(), -1).ToCharArray();

                    f.skip(4); // Meta data

                    model.skeleton.bones.Add(bone);
                }
                model.skeleton.reset();

/*                //Skeletal Animations
 *
 *              SkelAnimation anim = new SkelAnimation();
 *
 *              for (int index = 0; index < content.skeletalAnimationsPointerTableEntries; index++)
 *               {
 *                  f.seek(content.skeletalAnimationsPointerTableOffset + (index * 4));
 *                  int dataOffset = f.readInt();
 *                  f.seek(dataOffset);
 *
 *                  RenderBase.OSkeletalAnimation skeletalAnimation = new RenderBase.OSkeletalAnimation();
 *
 *                  skeletalAnimation.name = f.readString(f.readInt(), -1);
 *                  int animationFlags = f.readInt();
 *                  skeletalAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
 *                  skeletalAnimation.frameSize = f.readFloat();
 *                  int boneTableOffset = f.readInt();
 *                  int boneTableEntries = f.readInt();
 *                  int metaDataPointerOffset = f.readInt();
 *
 * //                    /*if (metaDataPointerOffset != 0)
 * //                    {
 * //                        data.Seek(metaDataPointerOffset, SeekOrigin.Begin);
 * //                        skeletalAnimation.userData = getMetaData(input);
 * //                    }
 *
 *                  for (int i = 0; i < boneTableEntries; i++)
 *                  {
 *                      f.seek(boneTableOffset + (i * 4));
 *                      int offset = f.readInt();
 *
 *                      RenderBase.OSkeletalAnimationBone bone = new RenderBase.OSkeletalAnimationBone();
 *
 *                      f.seek(offset);
 *                      bone.name = f.readString(f.readInt(), -1);
 *                      int animationTypeFlags = f.readInt();
 *
 *                      int flags = f.readInt();
 *
 *                      RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);
 *                                      switch (segmentType)
 *                                      {
 *                          case RenderBase.OSegmentType.transform:
 *                              f.seek(offset + 0x18);
 *
 *                              uint notExistMask = 0x80000;
 *                              uint constantMask = 0x200;
 *
 *                              for (int j = 0; j < 2; j++)
 *                              {
 *                                  for (int axis = 0; axis < 3; axis++)
 *                                  {
 *                                      bool notExist = (flags & notExistMask) > 0;
 *                                      bool constant = (flags & constantMask) > 0;
 *
 *                                      RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();
 *                                      frame.exists = !notExist;
 *                                      if (frame.exists)
 *                                      {
 *                                          if (constant)
 *                                          {
 *                                              frame.interpolation = RenderBase.OInterpolationMode.linear;
 *                                              frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0));
 *                                          }
 *                                          else
 *                                          {
 *                                              int frameOffset = f.readInt();
 *                                              long position = data.Position;
 *                                              f.seek(frameOffset);
 *                                              getAnimationKeyFrame(input, frame);
 *                                              f.seek(position);
 *                                          }
 *                                      }
 *                                      else
 *                                          data.Seek(4, SeekOrigin.Current);
 *
 *                                      if (j == 0)
 *                                      {
 *                                          switch (axis)
 *                                          {
 *                                              case 0: bone.rotationX = frame; break;
 *                                              case 1: bone.rotationY = frame; break;
 *                                              case 2: bone.rotationZ = frame; break;
 *                                          }
 *                                      }
 *                                      else
 *                                      {
 *                                          switch (axis)
 *                                          {
 *                                              case 0: bone.translationX = frame; break;
 *                                              case 1: bone.translationY = frame; break;
 *                                              case 2: bone.translationZ = frame; break;
 *                                          }
 *                                      }
 *
 *                                      notExistMask <<= 1;
 *                                      constantMask <<= 1;
 *                                  }
 *
 *                                  constantMask <<= 1;
 *                              }
 *
 *                              break;
 *                          case RenderBase.OSegmentType.transformQuaternion:
 *                              bone.isFrameFormat = true;
 *
 *                              uint scaleOffset = input.ReadUInt32();
 *                              uint rotationOffset = input.ReadUInt32();
 *                              uint translationOffset = input.ReadUInt32();
 *
 *                              if ((flags & 0x20) == 0)
 *                              {
 *                                  bone.scale.exists = true;
 *                                  data.Seek(scaleOffset, SeekOrigin.Begin);
 *
 *                                  if ((flags & 4) > 0)
 *                                  {
 *                                      bone.scale.vector.Add(new RenderBase.OVector4(
 *                                          input.ReadSingle(),
 *                                          input.ReadSingle(),
 *                                          input.ReadSingle(),
 *                                          0));
 *                                  }
 *                                  else
 *                                  {
 *                                      bone.scale.startFrame = input.ReadSingle();
 *                                      bone.scale.endFrame = input.ReadSingle();
 *
 *                                      uint scaleFlags = input.ReadUInt32();
 *                                      uint scaleDataOffset = input.ReadUInt32();
 *                                      uint scaleEntries = input.ReadUInt32();
 *
 *                                      data.Seek(scaleDataOffset, SeekOrigin.Begin);
 *                                      for (int j = 0; j < scaleEntries; j++)
 *                                      {
 *                                          bone.scale.vector.Add(new RenderBase.OVector4(
 *                                              input.ReadSingle(),
 *                                              input.ReadSingle(),
 *                                              input.ReadSingle(),
 *                                              0));
 *                                      }
 *                                  }
 *                              }
 *
 *                              if ((flags & 0x10) == 0)
 *                                              {
 *                                                  bone.rotationQuaternion.exists = true;
 *                                                  data.Seek(rotationOffset, SeekOrigin.Begin);
 *
 *                                                  if ((flags & 2) > 0)
 *                                                  {
 *                                                      bone.rotationQuaternion.vector.Add(new RenderBase.OVector4(
 *                                                          input.ReadSingle(),
 *                                                          input.ReadSingle(),
 *                                                          input.ReadSingle(),
 *                                                          input.ReadSingle()));
 *                                                  }
 *                                                  else
 *                                                  {
 *                                                      bone.rotationQuaternion.startFrame = input.ReadSingle();
 *                                                      bone.rotationQuaternion.endFrame = input.ReadSingle();
 *
 *                                                      uint rotationFlags = input.ReadUInt32();
 *                                                      uint rotationDataOffset = input.ReadUInt32();
 *                                                      uint rotationEntries = input.ReadUInt32();
 *
 *                                                      data.Seek(rotationDataOffset, SeekOrigin.Begin);
 *                                                      for (int j = 0; j < rotationEntries; j++)
 *                                                      {
 *                                                          bone.rotationQuaternion.vector.Add(new RenderBase.OVector4(
 *                                                              input.ReadSingle(),
 *                                                              input.ReadSingle(),
 *                                                              input.ReadSingle(),
 *                                                              input.ReadSingle()));
 *                                                      }
 *                                                  }
 *                                              }
 *
 *                                              if ((flags & 8) == 0)
 *                                              {
 *                                                  bone.translation.exists = true;
 *                                                  data.Seek(translationOffset, SeekOrigin.Begin);
 *
 *                                                  if ((flags & 1) > 0)
 *                                                  {
 *                                                      bone.translation.vector.Add(new RenderBase.OVector4(
 *                                                          input.ReadSingle(),
 *                                                          input.ReadSingle(),
 *                                                          input.ReadSingle(),
 *                                                          0));
 *                                                  }
 *                                                  else
 *                                                  {
 *                                                      bone.translation.startFrame = input.ReadSingle();
 *                                                      bone.translation.endFrame = input.ReadSingle();
 *
 *                                                      uint translationFlags = input.ReadUInt32();
 *                                                      uint translationDataOffset = input.ReadUInt32();
 *                                                      uint translationEntries = input.ReadUInt32();
 *
 *                                                      data.Seek(translationDataOffset, SeekOrigin.Begin);
 *                                                      for (int j = 0; j < translationEntries; j++)
 *                                                      {
 *                                                          bone.translation.vector.Add(new RenderBase.OVector4(
 *                                                              input.ReadSingle(),
 *                                                              input.ReadSingle(),
 *                                                              input.ReadSingle(),
 *                                                              0));
 *                                                      }
 *                                                  }
 *                                              }
 *
 *                                              break;
 *                                          case RenderBase.OSegmentType.transformMatrix:
 *                                              bone.isFullBakedFormat = true;
 *
 *                                              input.ReadUInt32();
 *                                              input.ReadUInt32();
 *                                              uint matrixOffset = input.ReadUInt32();
 *                                              uint entries = input.ReadUInt32();
 *
 *                                              data.Seek(matrixOffset, SeekOrigin.Begin);
 *                                              for (int j = 0; j < entries; j++)
 *                                              {
 *                                                  RenderBase.OMatrix transform = new RenderBase.OMatrix();
 *                                                  transform.M11 = input.ReadSingle();
 *                                                  transform.M21 = input.ReadSingle();
 *                                                  transform.M31 = input.ReadSingle();
 *                                                  transform.M41 = input.ReadSingle();
 *
 *                                                  transform.M12 = input.ReadSingle();
 *                                                  transform.M22 = input.ReadSingle();
 *                                                  transform.M32 = input.ReadSingle();
 *                                                  transform.M42 = input.ReadSingle();
 *
 *                                                  transform.M13 = input.ReadSingle();
 *                                                  transform.M23 = input.ReadSingle();
 *                                                  transform.M33 = input.ReadSingle();
 *                                                  transform.M43 = input.ReadSingle();
 *
 *                                                  bone.transform.Add(transform);
 *                                              }
 *
 *                                              break;
 *                                          default: throw new Exception(string.Format("BCH: Unknow Segment Type {0} on Skeletal Animation bone {1}! STOP!", segmentType, bone.name));
 *                                      }
 *
 *                                      skeletalAnimation.bone.Add(bone);
 *                                  }
 *
 *                                  models.skeletalAnimation.list.Add(skeletalAnimation);
 *                              }
 *
 *                              //Material Animations
 *                              for (int index = 0; index < contentHeader.materialAnimationsPointerTableEntries; index++)
 *                              {
 *                                  data.Seek(contentHeader.materialAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin);
 *                                  uint dataOffset = input.ReadUInt32();
 *                                  data.Seek(dataOffset, SeekOrigin.Begin);
 *
 *                                  RenderBase.OMaterialAnimation materialAnimation = new RenderBase.OMaterialAnimation();
 *
 *                                  materialAnimation.name = readString(input);
 *                                  uint animationFlags = input.ReadUInt32();
 *                                  materialAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1);
 *                                  materialAnimation.frameSize = input.ReadSingle();
 *                                  uint dataTableOffset = input.ReadUInt32();
 *                                  uint dataTableEntries = input.ReadUInt32();
 *                                  input.ReadUInt32();
 *                                  uint textureNameTableOffset = input.ReadUInt32();
 *                                  uint textureNameTableEntries = input.ReadUInt32();
 *
 *                                  data.Seek(textureNameTableOffset, SeekOrigin.Begin);
 *                                  for (int i = 0; i < textureNameTableEntries; i++)
 *                                  {
 *                                      string name = readString(input);
 *                                      materialAnimation.textureName.Add(name);
 *                                  }
 *
 *                                  for (int i = 0; i < dataTableEntries; i++)
 *                                  {
 *                                      data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin);
 *                                      uint offset = input.ReadUInt32();
 *
 *                                      RenderBase.OMaterialAnimationData animationData = new RenderBase.OMaterialAnimationData();
 *
 *                                      data.Seek(offset, SeekOrigin.Begin);
 *                                      animationData.name = readString(input);
 *                                      uint animationTypeFlags = input.ReadUInt32();
 *                                      uint flags = input.ReadUInt32();
 *
 *                                      animationData.type = (RenderBase.OMaterialAnimationType)(animationTypeFlags & 0xff);
 *                                      RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf);
 *
 *                                      int segmentCount = 0;
 *                                      switch (segmentType)
 *                                      {
 *                                          case RenderBase.OSegmentType.rgbaColor: segmentCount = 4; break;
 *                                          case RenderBase.OSegmentType.vector2: segmentCount = 2; break;
 *                                          case RenderBase.OSegmentType.single: segmentCount = 1; break;
 *                                          case RenderBase.OSegmentType.integer: segmentCount = 1; break;
 *                                      }
 *
 *                                      for (int j = 0; j < segmentCount; j++)
 *                                      {
 *                                          RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup();
 *
 *                                          data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin);
 *
 *                                          frame.exists = (flags & (0x100 << j)) == 0;
 *                                          bool constant = (flags & (1 << j)) > 0;
 *
 *                                          if (frame.exists)
 *                                          {
 *                                              if (constant)
 *                                              {
 *                                                  frame.interpolation = RenderBase.OInterpolationMode.linear;
 *                                                  frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0));
 *                                              }
 *                                              else
 *                                              {
 *                                                  uint frameOffset = input.ReadUInt32();
 *                                                  data.Seek(frameOffset, SeekOrigin.Begin);
 *                                                  getAnimationKeyFrame(input, frame);
 *                                              }
 *                                          }
 *
 *                                          animationData.frameList.Add(frame);
 *                                      }
 *
 *                                      materialAnimation.data.Add(animationData);
 *                                  }
 *
 *                                  models.materialAnimation.list.Add(materialAnimation);
 *                              }
 *
 */
            }
        }