Пример #1
0
 public static void Load_hTextures()
 {
     if (ADTTexData.textureBlockData.MTXP)
     {
         ADTTexData.textureBlockData.terrainHTextures = new Dictionary <string, ADTTexData.Texture2Ddata>();
         foreach (string texturePath in ADTTexData.textureBlockData.terrainTexturePaths)
         {
             string noExtension   = Path.GetFileNameWithoutExtension(texturePath);
             string directoryPath = Path.GetDirectoryName(texturePath);
             string hTexturePath  = directoryPath + @"\" + noExtension + "_h" + ".blp";
             if (Casc.FileExists(hTexturePath))
             {
                 string  extractedPath = Casc.GetFile(hTexturePath);
                 Stream  stream        = File.Open(extractedPath, FileMode.Open);
                 BLP     blp           = new BLP();
                 byte[]  data          = blp.GetUncompressed(stream, true);
                 BLPinfo info          = blp.Info();
                 ADTTexData.Texture2Ddata texture2Ddata = new ADTTexData.Texture2Ddata();
                 texture2Ddata.hasMipmaps = info.hasMipmaps;
                 texture2Ddata.width      = info.width;
                 texture2Ddata.height     = info.height;
                 if (info.width != info.height) // Unity doesn't support nonsquare mipmaps // sigh
                 {
                     texture2Ddata.hasMipmaps = false;
                 }
                 texture2Ddata.textureFormat = info.textureFormat;
                 texture2Ddata.TextureData   = data;
                 ADTTexData.textureBlockData.terrainHTextures.Add(texturePath, texture2Ddata);
                 stream.Close();
                 stream = null;
             }
         }
     }
 }
Пример #2
0
    public static TextureData Open(uint fileDataId)
    {
        if (fileDataId == 0)
        {
            return(null);
        }

        var stream = CASC.OpenFile(fileDataId);

        if (stream == null)
        {
            return(null);
        }

        var textureData = new TextureData();
        var blp         = new BLP();
        var blpData     = blp.GetUncompressed(stream);
        var blpInfo     = blp.GetInfo();

        textureData.HasMipmaps    = blpInfo.hasMipmaps;
        textureData.Width         = blpInfo.width;
        textureData.Height        = blpInfo.height;
        textureData.RawData       = blpData;
        textureData.TextureFormat = blpInfo.textureFormat;

        return(textureData);
    }
Пример #3
0
 public static void Load_hTextures(CASCHandler Handler)
 {
     if (ADTTexData.textureBlockData.MTXP)
     {
         ADTTexData.textureBlockData.terrainHTextures = new Dictionary <uint, ADTTexData.Texture2Ddata>();
         foreach (uint TextureFileDataId in ADTTexData.textureBlockData.terrainTextureFileDataIds)
         {
             using (var stream = Handler.OpenFile(TextureFileDataId))
             {
                 BLP     blp  = new BLP();
                 byte[]  data = blp.GetUncompressed(stream, true);
                 BLPinfo info = blp.Info();
                 ADTTexData.Texture2Ddata texture2Ddata = new ADTTexData.Texture2Ddata();
                 texture2Ddata.hasMipmaps = info.hasMipmaps;
                 texture2Ddata.width      = info.width;
                 texture2Ddata.height     = info.height;
                 if (info.width != info.height) // Unity doesn't support nonsquare mipmaps // sigh
                 {
                     texture2Ddata.hasMipmaps = false;
                 }
                 texture2Ddata.textureFormat = info.textureFormat;
                 texture2Ddata.TextureData   = data;
                 ADTTexData.textureBlockData.terrainHTextures.Add(TextureFileDataId, texture2Ddata);
                 stream.Close();
             }
         }
     }
 }
Пример #4
0
        public static void ReadTXID(BinaryReader reader, M2Model model, uint chunkSize)
        {
            var txidSize = chunkSize / 4;

            for (var i = 0; i < txidSize; ++i)
            {
                var fileDataId = reader.ReadUInt32();

                if (!TextureFileIds.Contains(fileDataId))
                {
                    var m2Texture   = new M2Texture();
                    var textureData = new TextureData();

                    using (var blpStream = CASC.OpenFile(fileDataId))
                    {
                        var blp     = new BLP();
                        var blpData = blp.GetUncompressed(blpStream);
                        var blpInfo = blp.GetInfo();

                        textureData.HasMipmaps    = blpInfo.hasMipmaps;
                        textureData.Width         = blpInfo.width;
                        textureData.Height        = blpInfo.height;
                        textureData.RawData       = blpData;
                        textureData.TextureFormat = blpInfo.textureFormat;

                        m2Texture.TextureData = textureData;
                        m2Texture.FileDataId  = fileDataId;

                        TextureFileIds.Add(fileDataId);
                    }

                    model.Textures.Add(m2Texture);
                }
            }
        }
Пример #5
0
    // texture paths //
    public static void ReadMOTX(Stream WMOrootstream, int MOTXsize)
    {
        long currentPos = WMOrootstream.Position;

        while (WMOrootstream.Position < currentPos + MOTXsize)
        {
            int    position = (int)(WMOrootstream.Position - currentPos);
            string path     = ReadNullTerminatedString(WMOrootstream);
            if (path != "") //&& !wmoData.textureData.ContainsKey(path))
            {
                //Debug.Log(path);
                wmoData.texturePaths.Add(position, path);
                string        extractedPath = Casc.GetFile(path);
                Stream        stream        = File.Open(extractedPath, FileMode.Open);
                byte[]        data          = BLP.GetUncompressed(stream, true);
                BLPinfo       info          = BLP.Info();
                Texture2Ddata texture2Ddata = new Texture2Ddata();
                texture2Ddata.hasMipmaps = info.hasMipmaps;
                texture2Ddata.width      = info.width;
                texture2Ddata.height     = info.height;
                //if (info.width != info.height) // Unity doesn't support nonsquare mipmaps // sigh
                //    texture2Ddata.hasMipmaps = false;
                texture2Ddata.textureFormat = info.textureFormat;
                texture2Ddata.TextureData   = data;
                wmoData.textureData[path]   = texture2Ddata;
                stream.Close();
                stream = null;
            }
        }
    } // loaded
Пример #6
0
    public static void ReadTXID(BinaryReader br, M2Data m2Data, CASCHandler CascHandler, uint Size)
    {
        LoadedBLPFileDataIds.Clear();
        var numTextures = Size / 4;

        for (int i = 0; i < numTextures; i++)
        {
            uint texture = br.ReadUInt32();

            M2Texture     m2Texture     = new M2Texture();
            Texture2Ddata texture2Ddata = new Texture2Ddata();
            if (!LoadedBLPFileDataIds.Contains(texture))
            {
                var     stream = CascHandler.OpenFile(texture);
                BLP     blp    = new BLP();
                byte[]  data   = blp.GetUncompressed(stream, true);
                BLPinfo info   = blp.Info();

                texture2Ddata.hasMipmaps    = info.hasMipmaps;
                texture2Ddata.width         = info.width;
                texture2Ddata.height        = info.height;
                texture2Ddata.textureFormat = info.textureFormat;
                texture2Ddata.TextureData   = data;

                m2Texture.texture2Ddata = texture2Ddata;
                m2Texture.FileDataId    = texture;

                stream.Close();
                stream.Dispose();

                LoadedBLPFileDataIds.Add(texture);
            }
            m2Data.m2Tex.Add(m2Texture);
        }
    }
Пример #7
0
    public static void Load(string mapName, Vector2 coords)
    {
        string          dataPath             = @"world\maptextures\" + mapName + @"\" + mapName + "_" + coords.x + "_" + coords.y + ".blp";
        MapTextureBlock mapTextureBlock      = new MapTextureBlock();
        Texture2Ddata   texture2Ddata        = new Texture2Ddata();
        string          extractedTexturePath = Casc.GetFile(dataPath);

        using (Stream stream = File.Open(extractedTexturePath, FileMode.Open))
        {
            BLP     blp  = new BLP();
            byte[]  data = blp.GetUncompressed(stream, true);
            BLPinfo info = blp.Info();
            texture2Ddata.hasMipmaps    = info.hasMipmaps;
            texture2Ddata.height        = info.height;
            texture2Ddata.width         = info.width;
            texture2Ddata.textureFormat = info.textureFormat;
            texture2Ddata.TextureData   = data;
            mapTextureBlock.dataPath    = dataPath;
            mapTextureBlock.mapName     = mapName;
            mapTextureBlock.coords      = coords;
            mapTextureBlock.data        = texture2Ddata;
            if (ADT.working)
            {
                MapTextureDataQueue.Enqueue(mapTextureBlock);
            }
            MapTextureThreadRunning = false;
        }
    }
Пример #8
0
        // materials //
        public static void ReadMOMT(BinaryReader reader, int MOMTsize, CASCHandler Handler)
        {
            wmoData.Info.nMaterials = MOMTsize / 64;
            for (int i = 0; i < wmoData.Info.nMaterials; i++)
            {
                WMOMaterial material = new WMOMaterial();

                material.flags      = reader.ReadMaterialFlags();
                material.ShaderType = (WMOFragmentShader)reader.ReadUInt32();           // Index into CMapObj::s_wmoShaderMetaData. See below (shader types).
                material.BlendMode  = (BlendingMode)reader.ReadUInt32();                // Blending: see https://wowdev.wiki/Rendering#EGxBlend

                material.TextureId1     = reader.ReadUInt32();                          // offset into MOTX; ≥ Battle (8.1.0.27826) No longer references MOTX but is a filedata id directly.
                material.SidnColor      = reader.ReadBGRA();                            // emissive color; see below (emissive color)
                material.FrameSidnColor = reader.ReadBGRA();                            // sidn emissive color; set at runtime; gets sidn-manipulated emissive color; see below (emissive color)
                material.TextureId2     = reader.ReadUInt32();                          // Environment Texture; envNameIndex; offset into MOTX
                material.DiffColor      = reader.ReadBGRA();                            // diffuse color; CWorldView::GatherMapObjDefGroupLiquids(): geomFactory->SetDiffuseColor((CImVectorⁱ*)(smo+7));
                                                                                        // environment textures don't need flags

                material.GroundType     = reader.ReadUInt32();                          // foreign_keyⁱ< uint32_t, &TerrainTypeRec::m_ID > ground_type; // according to CMapObjDef::GetGroundType
                material.TextureId3     = reader.ReadUInt32();                          // offset into MOTX
                material.Color          = reader.ReadBGRA();
                material.texture3_flags = reader.ReadMaterialFlags();

                // skip runtime data //
                reader.BaseStream.Seek(16, SeekOrigin.Current);

                wmoData.materials.Add(material);
                if (!wmoData.texturePaths.ContainsKey(material.TextureId1))
                {
                    wmoData.texturePaths.Add(material.TextureId1, material.TextureId1);
                }

                if (material.TextureId1 != 0)
                {
                    Texture2Ddata textureData = new Texture2Ddata();
                    Stream        stream      = Handler.OpenFile(material.TextureId1);
                    BLP           blp         = new BLP();
                    byte[]        data        = blp.GetUncompressed(stream, true);
                    BLPinfo       info        = blp.Info();
                    textureData.hasMipmaps    = info.hasMipmaps;
                    textureData.width         = info.width;
                    textureData.height        = info.height;
                    textureData.textureFormat = info.textureFormat;
                    textureData.TextureData   = data;
                    stream.Close();
                    stream.Dispose();
                    LoadedBLPs.Add(material.TextureId1);

                    if (!wmoData.textureData.ContainsKey(material.TextureId1))
                    {
                        wmoData.textureData.Add(material.TextureId1, textureData);
                    }
                }
            }
        }   // loaded
Пример #9
0
    private void AssignMinimapTexture(GameObject MinimapObject, string minimapName)
    {
        string path          = @"world\minimaps\" + minimapName + @"\" + MinimapObject.name + ".blp";
        string extractedPath = Casc.GetFile(path);
        Stream stream        = File.Open(extractedPath, FileMode.Open);

        byte[]    data = BLP.GetUncompressed(stream, false);
        BLPinfo   info = BLP.Info();
        Texture2D tex  = new Texture2D(info.width, info.height, BLP.TxFormat(), false);

        tex.LoadRawTextureData(data);
        MinimapObject.GetComponent <RawImage>().texture = tex;
        MinimapObject.GetComponent <RawImage>().uvRect  = new Rect(0, 0, 1, -1);
        tex.Apply();
        stream.Close();
        BLP.Close();
    }
Пример #10
0
    public void ReadMTEX(MemoryStream ADTtexstream, int MTEXsize)
    {
        if (ADTtexstream.Length == ADTtexstream.Position)
        {
            return;
        }

        // texture path strings, separated by 0
        string texturePath      = "";
        int    numberOfTextures = 0;

        for (int a = 0; a < MTEXsize; a++)
        {
            int b = ADTtexstream.ReadByte();
            if (b != 0)
            {
                var stringSymbol = System.Convert.ToChar(b);
                texturePath = texturePath + stringSymbol;
            }
            else if (b == 0)
            {
                ADTTexData.textureBlockData.terrainTexturePaths.Add(texturePath);
                string extractedPath = Casc.GetFile(texturePath);
                using (Stream stream = File.Open(extractedPath, FileMode.Open))
                {
                    BLP     blp  = new BLP();
                    byte[]  data = blp.GetUncompressed(stream, true);
                    BLPinfo info = blp.Info();
                    ADTTexData.Texture2Ddata texture2Ddata = new ADTTexData.Texture2Ddata();
                    texture2Ddata.hasMipmaps = info.hasMipmaps;
                    texture2Ddata.width      = info.width;
                    texture2Ddata.height     = info.height;
                    if (info.width != info.height) // Unity doesn't support nonsquare mipmaps // sigh
                    {
                        texture2Ddata.hasMipmaps = false;
                    }
                    texture2Ddata.textureFormat = info.textureFormat;
                    texture2Ddata.TextureData   = data;
                    ADTTexData.textureBlockData.terrainTextures.Add(texturePath, texture2Ddata);
                    texturePath = null;
                    numberOfTextures++;
                }
            }
        }
    }
Пример #11
0
    // Request a minimap image from the parser //
    private static void RequestBlock(Minimap.MinimapRequest minimapRequest)
    {
        string mapName       = minimapRequest.mapName;
        string fileName      = "map" + minimapRequest.coords.x + "_" + minimapRequest.coords.y + ".blp";
        string path          = @"world\minimaps\" + mapName + @"\" + fileName;
        string extractedPath = Casc.GetFile(path);

        using (Stream stream = File.Open(extractedPath, FileMode.Open))
        {
            BLP     blp  = new BLP();
            byte[]  data = blp.GetUncompressed(stream, true);
            BLPinfo info = blp.Info();
            MinimapData.MinimapBlockData blockData = new MinimapData.MinimapBlockData();
            blockData.mapName         = mapName;
            blockData.coords          = minimapRequest.coords;
            blockData.textureInfo     = info;
            blockData.minimapByteData = data;

            MinimapData.MinimapDataQueue.Enqueue(blockData);
        }
    }
Пример #12
0
    // texture paths //
    public static void ReadMOTX(Stream WMOrootstream, int MOTXsize)
    {
        long currentPos = WMOrootstream.Position;

        while (WMOrootstream.Position < currentPos + MOTXsize)
        {
            int    position = (int)(WMOrootstream.Position - currentPos);
            string path     = ReadNullTerminatedString(WMOrootstream);
            if (path != "") //&& !wmoData.textureData.ContainsKey(path))
            {
                wmoData.texturePaths.Add(position, path);
                if (!LoadedBLPs.Contains(path))
                {
                    string extractedPath = Casc.GetFile(path);
                    if (File.Exists(extractedPath))
                    {
                        Stream        stream        = File.Open(extractedPath, FileMode.Open);
                        BLP           blp           = new BLP();
                        byte[]        data          = blp.GetUncompressed(stream, true);
                        BLPinfo       info          = blp.Info();
                        Texture2Ddata texture2Ddata = new Texture2Ddata();
                        texture2Ddata.hasMipmaps    = info.hasMipmaps;
                        texture2Ddata.width         = info.width;
                        texture2Ddata.height        = info.height;
                        texture2Ddata.textureFormat = info.textureFormat;
                        texture2Ddata.TextureData   = data;
                        wmoData.textureData[path]   = texture2Ddata;
                        stream.Close();
                        stream.Dispose();
                        stream = null;
                    }
                    LoadedBLPs.Add(path);
                }
            }
        }
    } // loaded
Пример #13
0
    public static void ReadMD21(MemoryStream ms, M2Data m2Data, M2Texture m2Tex)
    {
        long md20position = ms.Position;

        StreamTools s                               = new StreamTools();
        int         MD20                            = s.ReadLong(ms);    // "MD20". Legion uses a chunked file format starting with MD21.
        int         version                         = s.ReadLong(ms);
        M2Array     name                            = s.ReadM2Array(ms); // should be globally unique, used to reload by name in internal clients
        var         flags                           = s.ReadLong(ms);
        M2Array     global_loops                    = s.ReadM2Array(ms); // Timestamps used in global looping animations.
        M2Array     sequences                       = s.ReadM2Array(ms); // Information about the animations in the model.
        M2Array     sequences_lookups               = s.ReadM2Array(ms); // Mapping of sequence IDs to the entries in the Animation sequences block.
        M2Array     bones                           = s.ReadM2Array(ms); // MAX_BONES = 0x100 => Creature\SlimeGiant\GiantSlime.M2 has 312 bones(Wrath)
        M2Array     key_bone_lookup                 = s.ReadM2Array(ms); // Lookup table for key skeletal bones.
        M2Array     vertices                        = s.ReadM2Array(ms);
        int         num_skin_profiles               = s.ReadLong(ms);
        M2Array     colors                          = s.ReadM2Array(ms); // Color and alpha animations definitions.
        M2Array     textures                        = s.ReadM2Array(ms);
        M2Array     texture_weights                 = s.ReadM2Array(ms); // Transparency of textures.
        M2Array     texture_transforms              = s.ReadM2Array(ms);
        M2Array     replaceable_texture_lookup      = s.ReadM2Array(ms);
        M2Array     materials                       = s.ReadM2Array(ms); // Blending modes / render flags.
        M2Array     bone_lookup_table               = s.ReadM2Array(ms);
        M2Array     texture_lookup_table            = s.ReadM2Array(ms);
        M2Array     tex_unit_lookup_table           = s.ReadM2Array(ms); // ≥ Cata: unused
        M2Array     transparency_lookup_table       = s.ReadM2Array(ms);
        M2Array     texture_transforms_lookup_table = s.ReadM2Array(ms);

        m2Data.bounding_box = s.ReadBoundingBox(ms);                    // min/max( [1].z, 2.0277779f ) - 0.16f seems to be the maximum camera height
        float       bounding_sphere_radius  = s.ReadFloat(ms);          // detail doodad draw dist = clamp (bounding_sphere_radius * detailDoodadDensityFade * detailDoodadDist, …)
        BoundingBox collision_box           = s.ReadBoundingBox(ms);
        float       collision_sphere_radius = s.ReadFloat(ms);

        M2Array collision_triangles     = s.ReadM2Array(ms);
        M2Array collision_vertices      = s.ReadM2Array(ms);
        M2Array collision_normals       = s.ReadM2Array(ms);
        M2Array attachments             = s.ReadM2Array(ms);            // position of equipped weapons or effects
        M2Array attachment_lookup_table = s.ReadM2Array(ms);
        M2Array events              = s.ReadM2Array(ms);                // Used for playing sounds when dying and a lot else.
        M2Array lights              = s.ReadM2Array(ms);                // Lights are mainly used in loginscreens but in wands and some doodads too.
        M2Array cameras             = s.ReadM2Array(ms);                // The cameras are present in most models for having a model in the character tab.
        M2Array camera_lookup_table = s.ReadM2Array(ms);
        M2Array ribbon_emitters     = s.ReadM2Array(ms);                // Things swirling around. See the CoT-entrance for light-trails.
        M2Array particle_emitters   = s.ReadM2Array(ms);

        // Name //
        ms.Position = name.offset + md20position;
        for (int n = 0; n < name.size; n++)
        {
            m2Data.name += Convert.ToChar(ms.ReadByte());
        }


        // Bones //
        ms.Position = bones.offset + md20position;
        M2TrackBase[] translationM2track = new M2TrackBase[bones.size];
        M2TrackBase[] rotationM22track   = new M2TrackBase[bones.size];
        M2TrackBase[] scaleM22track      = new M2TrackBase[bones.size];
        for (int cb = 0; cb < bones.size; cb++)
        {
            M2CompBone m2CompBone = new M2CompBone();

            m2CompBone.key_bone_id      = s.ReadLong(ms);               // Back-reference to the key bone lookup table. -1 if this is no key bone.
            m2CompBone.flags            = s.ReadUint32(ms);
            m2CompBone.parent_bone      = s.ReadShort(ms);
            m2CompBone.submesh_id       = s.ReadUint16(ms);
            m2CompBone.uDistToFurthDesc = s.ReadUint16(ms);
            m2CompBone.uZRatioOfChain   = s.ReadUint16(ms);

            translationM2track[cb] = s.ReadM2Track(ms);
            rotationM22track[cb]   = s.ReadM2Track(ms);
            scaleM22track[cb]      = s.ReadM2Track(ms);

            Vector3 pivotRaw = new Vector3(s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale);
            m2CompBone.pivot = new Vector3(-pivotRaw.x, pivotRaw.z, -pivotRaw.y);

            m2Data.m2CompBone.Add(m2CompBone);
        }


        // Animations //
        int numberOfAnimations = 0;

        for (int ab = 0; ab < bones.size; ab++)
        {
            List <Animation_Vector3>    bone_position_animations = new List <Animation_Vector3>();
            List <Animation_Quaternion> bone_rotation_animations = new List <Animation_Quaternion>();
            List <Animation_Vector3>    bone_scale_animations    = new List <Animation_Vector3>();

            // Position //
            int numberOfPositionAnimations = translationM2track[ab].Timestamps.size;
            if (numberOfAnimations < numberOfPositionAnimations)
            {
                numberOfAnimations = numberOfPositionAnimations;
            }
            for (int at = 0; at < numberOfPositionAnimations; at++)
            {
                Animation         bone_animation = new Animation();
                Animation_Vector3 positions      = new Animation_Vector3();

                // Timestamps //
                List <int> timeStamps = new List <int>();
                ms.Position = translationM2track[ab].Timestamps.offset + md20position;
                M2Array m2AnimationOffset = s.ReadM2Array(ms);
                ms.Position = m2AnimationOffset.offset;
                for (int t = 0; t < m2AnimationOffset.size; t++)
                {
                    timeStamps.Add(s.ReadLong(ms));
                }
                positions.timeStamps = timeStamps;

                // Values //
                List <Vector3> values = new List <Vector3>();
                ms.Position = translationM2track[ab].Values.offset + md20position;
                M2Array m2AnimationValues = s.ReadM2Array(ms);
                ms.Position = m2AnimationValues.offset;
                for (int t = 0; t < m2AnimationValues.size; t++)
                {
                    Vector3 rawPosition = new Vector3(s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale);
                    values.Add(new Vector3(-rawPosition.x, rawPosition.z, -rawPosition.y));
                }
                positions.values = values;
                bone_position_animations.Add(positions);
            }


            // Rotation //
            int numberOfRotationAnimations = rotationM22track[ab].Timestamps.size;
            if (numberOfAnimations < numberOfRotationAnimations)
            {
                numberOfAnimations = numberOfRotationAnimations;
            }
            for (int ar = 0; ar < numberOfRotationAnimations; ar++)
            {
                Animation_Quaternion rotations = new Animation_Quaternion();

                // Timestamps //
                List <int> timeStamps = new List <int>();
                ms.Position = rotationM22track[ab].Timestamps.offset + md20position;
                M2Array m2AnimationOffset = s.ReadM2Array(ms);
                ms.Position = m2AnimationOffset.offset;
                for (int t = 0; t < m2AnimationOffset.size; t++)
                {
                    timeStamps.Add(s.ReadLong(ms));
                }
                rotations.timeStamps = timeStamps;

                // Values //
                List <Quaternion> values = new List <Quaternion>();
                ms.Position = rotationM22track[ab].Values.offset + md20position;
                M2Array m2AnimationValues = s.ReadM2Array(ms);
                ms.Position = m2AnimationValues.offset;
                for (int t = 0; t < m2AnimationValues.size; t++)
                {
                    Quaternion rawRotation = s.ReadQuaternion16(ms);
                    values.Add(new Quaternion(rawRotation.x, rawRotation.y, rawRotation.z, rawRotation.w));
                }
                rotations.values = values;
                bone_rotation_animations.Add(rotations);
            }

            // Scale //
            int numberOfScaleAnimations = scaleM22track[ab].Timestamps.size;
            if (numberOfAnimations < numberOfScaleAnimations)
            {
                numberOfAnimations = numberOfScaleAnimations;
            }
            for (int aS = 0; aS < numberOfScaleAnimations; aS++)
            {
                Animation_Vector3 scales = new Animation_Vector3();

                // Timestamps //
                List <int> timeStamps = new List <int>();
                ms.Position = scaleM22track[ab].Timestamps.offset + md20position;
                M2Array m2AnimationOffset = s.ReadM2Array(ms);
                ms.Position = m2AnimationOffset.offset;
                for (int t = 0; t < m2AnimationOffset.size; t++)
                {
                    timeStamps.Add(s.ReadLong(ms));
                }
                scales.timeStamps = timeStamps;

                // Values //
                List <Vector3> values = new List <Vector3>();
                ms.Position = scaleM22track[ab].Values.offset + md20position;
                M2Array m2AnimationValues = s.ReadM2Array(ms);
                ms.Position = m2AnimationValues.offset;
                for (int t = 0; t < m2AnimationValues.size; t++)
                {
                    Vector3 rawScale = new Vector3(s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale);
                    values.Add(new Vector3(-rawScale.x, rawScale.z, -rawScale.y));
                }
                scales.values = values;
                bone_scale_animations.Add(scales);
            }
            //Debug.Log(numberOfPositionAnimations + " " + numberOfRotationAnimations + " " + numberOfScaleAnimations);
            m2Data.position_animations.Add(bone_position_animations);
            m2Data.rotation_animations.Add(bone_rotation_animations);
            m2Data.scale_animations.Add(bone_scale_animations);
        }
        m2Data.numberOfAnimations = numberOfAnimations;

        // Bone Lookup Table //
        ms.Position = bone_lookup_table.offset + md20position;
        for (int blt = 0; blt < key_bone_lookup.size; blt++)
        {
            m2Data.bone_lookup_table.Add(s.ReadUint16(ms));
        }

        // Key-Bone Lookup //
        ms.Position = key_bone_lookup.offset + md20position;
        for (int kbl = 0; kbl < key_bone_lookup.size; kbl++)
        {
            m2Data.key_bone_lookup.Add(s.ReadShort(ms));
        }

        // Vertices //
        ms.Position     = vertices.offset + md20position;
        m2Data.meshData = new MeshData();
        for (int v = 0; v < vertices.size; v++)
        {
            Vector3 rawPosition = new Vector3(s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale, s.ReadFloat(ms) / Settings.worldScale);
            m2Data.meshData.pos.Add(new Vector3(-rawPosition.x, rawPosition.z, -rawPosition.y));
            m2Data.meshData.bone_weights.Add(new float[] { ms.ReadByte() / 255.0f, ms.ReadByte() / 255.0f, ms.ReadByte() / 255.0f, ms.ReadByte() / 255.0f });
            m2Data.meshData.bone_indices.Add(new int[] { ms.ReadByte(), ms.ReadByte(), ms.ReadByte(), ms.ReadByte() });
            //Debug.Log(m2Data.meshData.bone_indices[v][0] + " " + m2Data.meshData.bone_indices[v][1] + " " + m2Data.meshData.bone_indices[v][2] + " " + m2Data.meshData.bone_indices[v][3]);
            Vector3 rawnormal = new Vector3(s.ReadFloat(ms) * Settings.worldScale, s.ReadFloat(ms) * Settings.worldScale, s.ReadFloat(ms) * Settings.worldScale);
            m2Data.meshData.normal.Add(new Vector3(-rawnormal.x, rawnormal.z, -rawnormal.y));
            m2Data.meshData.tex_coords.Add(new Vector2(s.ReadFloat(ms), s.ReadFloat(ms)));
            m2Data.meshData.tex_coords2.Add(new Vector2(s.ReadFloat(ms), s.ReadFloat(ms)));
        }

        // Textures //
        ms.Position = textures.offset + md20position;
        for (int t = 0; t < textures.size; t++)
        {
            M2Texture m2Texture = new M2Texture();

            m2Texture.type  = s.ReadLong(ms);
            m2Texture.flags = s.ReadLong(ms);

            M2Array filename = s.ReadM2Array(ms);

            // seek to filename and read //
            long savePosition = ms.Position;
            ms.Position = filename.offset + md20position;
            string fileNameString = "";
            for (int n = 0; n < filename.size; n++)
            {
                fileNameString += Convert.ToChar(ms.ReadByte());
            }
            ms.Position = savePosition;

            string fileNameStringFix = fileNameString.TrimEnd(fileNameString[fileNameString.Length - 1]);
            m2Texture.filename = fileNameStringFix;

            Texture2Ddata texture2Ddata = new Texture2Ddata();

            if (fileNameStringFix.Length > 1)
            {
                if (!LoadedBLPs.Contains(fileNameStringFix))
                {
                    string  extractedPath = Casc.GetFile(fileNameStringFix);
                    Stream  stream        = File.Open(extractedPath, FileMode.Open);
                    BLP     blp           = new BLP();
                    byte[]  data          = blp.GetUncompressed(stream, true);
                    BLPinfo info          = blp.Info();
                    texture2Ddata.hasMipmaps    = info.hasMipmaps;
                    texture2Ddata.width         = info.width;
                    texture2Ddata.height        = info.height;
                    texture2Ddata.textureFormat = info.textureFormat;
                    texture2Ddata.TextureData   = data;
                    m2Texture.texture2Ddata     = texture2Ddata;
                    stream.Close();
                    stream.Dispose();
                    stream = null;
                    LoadedBLPs.Add(fileNameString);
                }
            }
            m2Data.m2Tex.Add(m2Texture);
        }

        // texture_lookup_table //
        ms.Position = texture_lookup_table.offset + md20position;
        for (int tl = 0; tl < texture_lookup_table.size; tl++)
        {
            m2Data.textureLookupTable.Add(s.ReadUint16(ms));
        }
    }