public void RemoveLegionChunks()
        {
            int magic = ReadInt(0);

            if (magic != ChunkedWowFile.InvertedMagicToInt("MD20"))
            {
                int md20            = LocateMD20Chunk();
                int to_remove_start = 0;

                if (magic == ChunkedWowFile.InvertedMagicToInt("MD21"))
                {
                    to_remove_start = ReadInt(4);
                }

                if (md20 > 0)
                {
                    RemoveBytes(0, md20);
                }

                // remove extra chunks at the end
                if (to_remove_start > 0)
                {
                    RemoveBytes(to_remove_start, Data.Length - to_remove_start);
                }
            }
        }
        private void RetrieveMTEX()
        {
            // skip MAMP chunk
            int start_mtex = tex.IsChunk(0xC, ChunkedWowFile.MagicToInt("MAMP")) ? tex.ReadInt(0x10) + 0x14 : 0xC;

            // real size = size + 0x8 (header size)
            int size = tex.ReadInt(start_mtex + 4) + 0x8;

            // start MTEX
            adt.AddEmptyBytes(currentPos, size);

            tex.BlockCopy(start_mtex, adt, currentPos, size);
            // remove unecessary data
            tex.RemoveBytes(0x0, start_mtex + size);

            adt.WriteInt(0x1C, currentPos - 0x14);

            for (int i = currentPos + 0x8; i < currentPos + size; ++i)
            {
                if (adt.Data[i] == 0)
                {
                    nTexture++;
                }
            }

            currentPos += size;
        }
        private void CreateMCIN()
        {
            adt.AddEmptyBytes(0x54, 0x1008);
            adt.WriteInt(0x54, ChunkedWowFile.MagicToInt("MCIN"));
            adt.WriteInt(0x58, 0x1000); // chunk size
            adt.WriteInt(0x18, 0x40);

            // after mcin chunk
            currentPos = 0x1008 + 0x54;
        }
        private void CheckMFBO()
        {
            adt.RemoveUnwantedChunksUntil(currentPos, "MFBO");

            if (currentPos < adt.Size() - 4 && adt.ReadInt(currentPos) == ChunkedWowFile.MagicToInt("MFBO"))
            {
                adt.WriteInt(0x38, currentPos - 0x14);
                currentPos += adt.ReadInt(currentPos + 0x4) + 0x8;
            }
            else
            {
                adt.WriteInt(0x38, 0x0);
            }
        }
        public AdtConverter(string path, bool h2o, bool model)
        {
            file   = path;
            water  = h2o;
            models = model;

            adt = new ChunkedWowFile(path);
            obj = new ChunkedWowFile(path.Replace(".adt", "_obj0.adt"));
            tex = new ChunkedWowFile(path.Replace(".adt", "_tex0.adt"));

            // clean the folder
            Utils.DeleteFile(path.Replace(".adt", "_obj1.adt"));
            Utils.DeleteFile(path.Replace(".adt", "_lod.adt"));
        }
        private void RetrieveMTXF()
        {
            adt.WriteInt(0x40, currentPos - 0x14);

            int posMTXF = 0, posMTXP = 0;
            int i = 0;

            for (i = 0; i < tex.Size() - 8; i += tex.ReadInt(i + 4))
            {
                if (tex.IsChunk(i, "MTXF"))
                {
                    posMTXF = i;
                    break;
                }
                else if (tex.IsChunk(i, "MTXP"))
                {
                    posMTXP = i;
                    break;
                }
            }

            if (posMTXF > 0)
            {
                int size = tex.ReadInt(i + 0x4);
                adt.AddEmptyBytes(currentPos, 0x8 + size);
                adt.WriteInt(currentPos, ChunkedWowFile.MagicToInt("MTXF"));
                adt.WriteInt(currentPos + 0x4, size);
                currentPos += 0x8;

                for (int k = posMTXF + 0x8; k < posMTXF + 0x8 + size; k += 0x4)
                {
                    // only flag used in wotlk
                    adt.WriteInt(currentPos, tex.Data[k] & 0x1);
                    currentPos += 0x4;
                }
            }
            else if (posMTXP > 0)
            {
                int size      = tex.ReadInt(i + 0x4);
                int mtxf_size = size / 4;

                adt.AddEmptyBytes(currentPos, 0x8 + mtxf_size);
                adt.WriteInt(currentPos, ChunkedWowFile.MagicToInt("MTXF"));
                adt.WriteInt(currentPos + 0x4, mtxf_size);
                currentPos += 0x8;

                for (int k = posMTXP + 0x8; k < posMTXP + 0x8 + size; k += 0x10)
                {
                    // only flag used in wotlk
                    adt.WriteInt(currentPos, tex.Data[k] & 0x1);
                    currentPos += 0x4;
                }
            }
            else
            {
                adt.AddEmptyBytes(currentPos, 0x8 + nTexture * 4);
                adt.WriteInt(currentPos, ChunkedWowFile.MagicToInt("MTXF"));
                adt.WriteInt(currentPos + 0x4, nTexture * 4);
                currentPos += 0x8 + nTexture * 4;
            }
        }
        private void RetrieveMCNK(int id = 0)
        {
            adt.RemoveUnwantedChunksUntil(currentPos, (int)chunks.MCNK);
            tex.RemoveUnwantedChunksUntil(0, (int)chunks.MCNK);
            obj.RemoveUnwantedChunksUntil(0, (int)chunks.MCNK);

            int            size_root_mcnk = adt.ReadInt(currentPos + 0x4);
            int            size_tex_mcnk  = tex.ReadInt(0x4);
            int            size_obj_mcnk  = obj.ReadInt(0x4);
            ChunkedWowFile root_mcnk      = new ChunkedWowFile(adt.Data, currentPos, size_root_mcnk + 0x8);
            ChunkedWowFile tex_mcnk       = new ChunkedWowFile(tex.Data, 8, size_tex_mcnk);
            ChunkedWowFile obj_mcnk       = new ChunkedWowFile(obj.Data, 8, size_obj_mcnk);

            // remove MCNK in split files
            tex.RemoveBytes(0, size_tex_mcnk + 8);
            obj.RemoveBytes(0, size_obj_mcnk + 8);

            Dictionary <int, int> adt_chunks = root_mcnk.ChunksOfs(0x88, (int)chunks.MCNK);
            Dictionary <int, int> tex_chunks = tex_mcnk.ChunksOfs(0, (int)chunks.MCNK);
            Dictionary <int, int> obj_chunks = obj_mcnk.ChunksOfs(0, (int)chunks.MCNK);

            int pos      = 0;
            int ofsMCVT  = 0x88;
            int sizeMCVT = (9 * 9 + 8 * 8) * 4;

            root_mcnk.WriteInt(pos + 0x3C, 0);

            uint flags = root_mcnk.ReadUInt(pos + 0x8);

            // fix high res hole
            if ((flags & 0x10000) != 0)
            {
                root_mcnk.WriteUShort(pos + 0x3C + 0x8, HighToLowResHole(root_mcnk.ReadULong(pos + 0x14 + 0x8)));
            }

            root_mcnk.WriteUInt(pos + 0x8, flags & 0xFFFF);

            pos += ofsMCVT + 0x8 + sizeMCVT;

            int ofsMCCV  = 0;
            int sizeMCCV = 0;

            if (adt_chunks.ContainsKey((int)chunks.MCCV))
            {
                ofsMCCV  = pos;
                sizeMCCV = root_mcnk.ReadInt(ofsMCCV + 0x4) + 0x8;
                pos     += sizeMCCV;
            }

            // remove MCLV
            if (adt_chunks.ContainsKey((int)chunks.MCLV))
            {
                root_mcnk.RemoveUnwantedChunksUntil(pos, (int)chunks.MCNR);
            }

            int ofsMCNR = pos;

            pos += 448 + 0x8;
            root_mcnk.WriteInt(ofsMCNR + 0x4, 435);

            int ofsMCLY  = 0;
            int sizeMCLY = 0;

            int nLayer = 0;

            List <int> mcal_offsets = new List <int>();

            if (tex_chunks.ContainsKey((int)chunks.MCLY))
            {
                tex_mcnk.RemoveUnwantedChunksUntil(0, (int)chunks.MCLY);
                ofsMCLY = pos;
                int size = tex_mcnk.ReadInt(0x4);
                sizeMCLY = size + 0x8;
                root_mcnk.AddEmptyBytes(pos, sizeMCLY);
                tex_mcnk.BlockCopy(0, root_mcnk, pos, sizeMCLY);
                tex_mcnk.RemoveBytes(0, sizeMCLY);

                nLayer = size / 0x10;

                int layer_pos = pos + 0x8;
                for (int i = 0; i < nLayer; ++i)
                {
                    uint groundEffect = root_mcnk.ReadUInt(layer_pos + 0xC);
                    if (groundEffect > 73186) // max wotlk id in GroundEffectTexture
                    {
                        root_mcnk.WriteInt(layer_pos + 0xC, 0);
                    }
                    root_mcnk.WriteUInt(layer_pos + 0x4, root_mcnk.ReadUInt(layer_pos + 0x4) & 0x7FF);
                    mcal_offsets.Add(root_mcnk.ReadInt(layer_pos + 0x8));

                    layer_pos += 0x10;
                }

                if (nLayer > 4)
                {
                    root_mcnk.RemoveBytes(pos + 0x8 + 64, 16 * (nLayer - 4));
                    sizeMCLY = 64 + 0x8;
                    root_mcnk.WriteInt(pos + 0x4, 64);
                    nLayer = 4;
                }
                pos += sizeMCLY;
            }

            int ofsMCRF  = pos;
            int sizeMCRF = 0x8;

            int nDoodads = 0, nMapObjRefs = 0;

            root_mcnk.AddEmptyBytes(pos, 8);
            root_mcnk.WriteInt(pos, ChunkedWowFile.MagicToInt("MCRF"));
            pos += 0x8;

            if (models)
            {
                if (obj_chunks.ContainsKey((int)chunks.MCRD))
                {
                    obj_mcnk.RemoveUnwantedChunksUntil(0, (int)chunks.MCRD);
                    int sizeMCRD = obj_mcnk.ReadInt(0x4);
                    root_mcnk.AddEmptyBytes(pos, sizeMCRD);
                    obj_mcnk.BlockCopy(0x8, root_mcnk, pos, sizeMCRD);
                    pos      += sizeMCRD;
                    nDoodads  = sizeMCRD / 4;
                    sizeMCRF += sizeMCRD;
                    obj_mcnk.RemoveBytes(0, 0x8 + sizeMCRD);
                }

                if (obj_chunks.ContainsKey((int)chunks.MCRW))
                {
                    obj_mcnk.RemoveUnwantedChunksUntil(0, (int)chunks.MCRW);
                    int sizeMCRW = obj_mcnk.ReadInt(0x4);
                    root_mcnk.AddEmptyBytes(pos, sizeMCRW);
                    obj_mcnk.BlockCopy(0x8, root_mcnk, pos, sizeMCRW);
                    pos        += sizeMCRW;
                    nMapObjRefs = sizeMCRW / 4;
                    sizeMCRF   += sizeMCRW;
                    obj_mcnk.RemoveBytes(0, 0x8 + sizeMCRW);
                }
            }

            // update MCRF size
            root_mcnk.WriteInt(ofsMCRF + 0x4, sizeMCRF - 0x8);

            // MCSH
            int ofsMCSH  = 0;
            int sizeMCSH = 0;

            if (tex_chunks.ContainsKey((int)chunks.MCSH))
            {
                ofsMCSH = pos;
                tex_mcnk.RemoveUnwantedChunksUntil(0, (int)chunks.MCSH);
                sizeMCSH = tex_mcnk.ReadInt(0x4) + 0x8;
                root_mcnk.AddEmptyBytes(pos, sizeMCSH);
                tex_mcnk.BlockCopy(0, root_mcnk, pos, sizeMCSH);
                pos += sizeMCSH;
                tex_mcnk.RemoveBytes(0, sizeMCSH);
            }


            // MCAL
            int ofsMCAL  = 0;
            int sizeMCAL = 0;

            ofsMCAL = pos;

            if (tex_chunks.ContainsKey((int)chunks.MCAL))
            {
                tex_mcnk.RemoveUnwantedChunksUntil(0, (int)chunks.MCAL);
                sizeMCAL = tex_mcnk.ReadInt(0x4) + 0x8;
                root_mcnk.AddEmptyBytes(pos, sizeMCAL);
                tex_mcnk.BlockCopy(0, root_mcnk, pos, sizeMCAL);
                tex_mcnk.RemoveBytes(0, sizeMCAL);

                if (mcal_offsets.Count() > 4)
                {
                    int size   = sizeMCAL - 0x8;
                    int target = mcal_offsets[4];
                    root_mcnk.RemoveBytes(pos + 0x8 + target, size - target);
                    sizeMCAL = target + 0x8;
                    root_mcnk.WriteInt(pos + 0x4, target);
                }

                pos += sizeMCAL;
            }
            if (sizeMCAL == 0)
            {
                root_mcnk.AddEmptyBytes(pos, 8);
                root_mcnk.WriteInt(pos, (int)chunks.MCAL);
                sizeMCAL = 0x8;
                pos     += 8;
            }

            // MCSE
            int ofsMCSE       = pos;
            int sizeMCSE      = 0;
            int nSoundEmitter = 0;

            if (adt_chunks.ContainsKey((int)chunks.MCSE))
            {
                root_mcnk.RemoveUnwantedChunksUntil(pos, (int)chunks.MCSE);
                sizeMCSE      = root_mcnk.ReadInt(pos + 0x4) + 0x8;
                nSoundEmitter = (sizeMCSE - 8) / 0x1C;
                pos          += sizeMCSE;
            }
            else
            {
                ofsMCSE = 0;
            }



            adt.AddEmptyBytes(currentPos, root_mcnk.Size() - size_root_mcnk - 8);
            root_mcnk.BlockCopy(0, adt.Data, currentPos, root_mcnk.Size());

            FillMCIN(id, currentPos, root_mcnk.Size());

            adt.WriteInt(currentPos, ChunkedWowFile.MagicToInt("MCNK")); // should not be necessary
            adt.WriteInt(currentPos + 0x4, root_mcnk.Size() - 0x8);

            int ofsPos = currentPos + 0x8; // MCNK header for offsets

            // Update headers
            adt.WriteInt(ofsPos + 0x4, (id % 16));
            adt.WriteInt(ofsPos + 0x8, (id / 16));
            // nLayer
            adt.WriteInt(ofsPos + 0xC, nLayer);
            // nDoodads
            adt.WriteInt(ofsPos + 0x10, nDoodads);
            adt.WriteInt(ofsPos + 0x14, ofsMCVT);
            adt.WriteInt(ofsPos + 0x18, ofsMCNR);
            adt.WriteInt(ofsPos + 0x1C, ofsMCLY);
            adt.WriteInt(ofsPos + 0x20, ofsMCRF);
            adt.WriteInt(ofsPos + 0x24, ofsMCAL);
            // sizeAlpha
            adt.WriteInt(ofsPos + 0x28, sizeMCAL);
            adt.WriteInt(ofsPos + 0x2C, ofsMCSH);
            // size shadow
            adt.WriteInt(ofsPos + 0x30, sizeMCSH);
            // area id
            adt.WriteInt(ofsPos + 0x34, 0);
            // nMapObjRefs
            adt.WriteInt(ofsPos + 0x38, nMapObjRefs);
            adt.WriteInt(ofsPos + 0x50, 0);
            adt.WriteInt(ofsPos + 0x58, ofsMCSE);
            // nSoundEmitter
            adt.WriteInt(ofsPos + 0x5C, nSoundEmitter);
            adt.WriteInt(ofsPos + 0x60, 0); // MCLQ
            adt.WriteInt(ofsPos + 0x64, 0); // size Liquid
            adt.WriteInt(ofsPos + 0x74, ofsMCCV);
            adt.WriteInt(ofsPos + 0x78, 0);
            adt.WriteInt(ofsPos + 0x7C, 0);

            currentPos += root_mcnk.Size();
        }