Esempio n. 1
0
        public static bool ExtractSingleWmo(uint fileId)
        {
            // Copy files from archive
            string fileName = $"File{fileId:X8}.xxx";

            if (File.Exists(Program.wmoDirectory + fileName))
            {
                return(true);
            }

            bool file_ok = true;

            Console.WriteLine($"Extracting {fileName}");
            WMORoot froot = new WMORoot();

            if (!froot.open(fileId))
            {
                Console.WriteLine("Couldn't open RootWmo!!!");
                return(true);
            }

            using (BinaryWriter binaryWriter = new BinaryWriter(File.Open(Program.wmoDirectory + fileName, FileMode.Create, FileAccess.Write)))
            {
                froot.ConvertToVMAPRootWmo(binaryWriter);
                int Wmo_nVertices = 0;
                //printf("root has %d groups\n", froot->nGroups);
                for (int i = 0; i < froot.groupFileDataIDs.Count; ++i)
                {
                    WMOGroup fgroup = new WMOGroup();
                    if (!fgroup.open(froot.groupFileDataIDs[i]))
                    {
                        Console.WriteLine($"Could not open all Group file for: {fileId.ToString()}");
                        file_ok = false;
                        break;
                    }

                    Wmo_nVertices += fgroup.ConvertToVMAPGroupWmo(binaryWriter, froot, false);// preciseVectorData);
                }

                binaryWriter.Seek(8, SeekOrigin.Begin); // store the correct no of vertices
                binaryWriter.Write(Wmo_nVertices);

                // Delete the extracted file in the case of an error
                if (!file_ok)
                {
                    File.Delete(Program.wmoDirectory + fileName);
                }
            }

            return(true);
        }
Esempio n. 2
0
        public bool Init(uint mapId)
        {
            FilenameChunk mwmo = GetChunk("MWMO")?.As <FilenameChunk>();

            if (mwmo != null && mwmo.Filenames.Count > 0)
            {
                foreach (var filename in mwmo.Filenames)
                {
                    wmoInstanceNames.Add(filename);
                    VmapFile.ExtractSingleWmo(filename);
                }
            }

            MODF wmoChunk = GetChunk("MODF")?.As <MODF>();

            if (wmoChunk != null && wmoChunk.MapObjDefs.Length > 0)
            {
                foreach (var wmo in wmoChunk.MapObjDefs)
                {
                    if (wmo.Flags.HasAnyFlag(MODFFlags.EntryIsFileID))
                    {
                        string fileName = $"FILE{wmo.Id:X8}.xxx";
                        VmapFile.ExtractSingleWmo(fileName);
                        WMORoot.Extract(wmo, fileName, false, mapId, mapId, Program.DirBinWriter, null);

                        if (VmapFile.WmoDoodads.ContainsKey(fileName))
                        {
                            Model.ExtractSet(VmapFile.WmoDoodads[fileName], wmo, false, mapId, mapId, Program.DirBinWriter, null);
                        }
                    }
                    else
                    {
                        WMORoot.Extract(wmo, wmoInstanceNames[(int)wmo.Id], false, mapId, mapId, Program.DirBinWriter, null);
                        if (VmapFile.WmoDoodads.ContainsKey(wmoInstanceNames[(int)wmo.Id]))
                        {
                            Model.ExtractSet(VmapFile.WmoDoodads[wmoInstanceNames[(int)wmo.Id]], wmo, false, mapId, mapId, Program.DirBinWriter, null);
                        }
                    }
                }

                wmoInstanceNames.Clear();
            }

            return(true);
        }
Esempio n. 3
0
File: Wmo.cs Progetto: ywscr/Tools
        public bool open(uint fileId, WMORoot rootWmo)
        {
            Stream stream = Program.CascHandler.OpenFile((int)fileId);

            if (stream == null)
            {
                Console.WriteLine("No such file.");
                return(false);
            }

            using (BinaryReader reader = new BinaryReader(stream))
            {
                long fileLength = reader.BaseStream.Length;
                while (reader.BaseStream.Position < fileLength)
                {
                    string fourcc = reader.ReadStringFromChars(4, true);
                    uint   size   = reader.ReadUInt32();

                    if (fourcc == "MOGP")//Fix sizeoff = Data size.
                    {
                        size = 68;
                    }

                    long nextpos = reader.BaseStream.Position + size;

                    if (fourcc == "MOGP")//header
                    {
                        groupName     = reader.ReadInt32();
                        descGroupName = reader.ReadInt32();
                        mogpFlags     = reader.ReadInt32();

                        for (var i = 0; i < 3; ++i)
                        {
                            bbcorn1[i] = reader.ReadSingle();
                        }

                        for (var i = 0; i < 3; ++i)
                        {
                            bbcorn2[i] = reader.ReadSingle();
                        }

                        moprIdx     = reader.ReadUInt16();
                        moprNItems  = reader.ReadUInt16();
                        nBatchA     = reader.ReadUInt16();
                        nBatchB     = reader.ReadUInt16();
                        nBatchC     = reader.ReadUInt32();
                        fogIdx      = reader.ReadUInt32();
                        groupLiquid = reader.ReadUInt32();
                        groupWMOID  = reader.ReadUInt32();

                        // according to WoW.Dev Wiki:
                        if (Convert.ToBoolean(rootWmo.flags & 4))
                        {
                            groupLiquid = GetLiquidTypeId(groupLiquid);
                        }
                        else if (groupLiquid == 15)
                        {
                            groupLiquid = 0;
                        }
                        else
                        {
                            groupLiquid = GetLiquidTypeId(groupLiquid + 1);
                        }

                        if (groupLiquid != 0)
                        {
                            liquflags |= 2;
                        }
                    }
                    else if (fourcc == "MOPY")
                    {
                        mopy_size  = (int)size;
                        nTriangles = (int)size / 2;
                        MOPY       = reader.ReadString((int)size);
                    }
                    else if (fourcc == "MOVI")
                    {
                        MOVI = new ushort[size / 2];
                        for (var i = 0; i < size / 2; ++i)
                        {
                            MOVI[i] = reader.ReadUInt16();
                        }
                    }
                    else if (fourcc == "MOVT")
                    {
                        MOVT = new float[size / 4];
                        for (var i = 0; i < size / 4; ++i)
                        {
                            MOVT[i] = reader.ReadSingle();
                        }

                        nVertices = size / 12;
                    }
                    else if (fourcc == "MONR")
                    {
                    }
                    else if (fourcc == "MOTV")
                    {
                    }
                    else if (fourcc == "MOBA")
                    {
                        MOBA      = new ushort[size / 2];
                        moba_size = (int)(size / 2);

                        for (var i = 0; i < size / 2; ++i)
                        {
                            MOBA[i] = reader.ReadUInt16();
                        }
                    }
                    else if (fourcc == "MODR")
                    {
                        for (var i = 0; i < size / 2; ++i)
                        {
                            DoodadReferences.Add(reader.Read <ushort>());
                        }
                    }
                    else if (fourcc == "MLIQ")
                    {
                        liquflags  |= 1;
                        hlq         = reader.Read <WMOLiquidHeader>();
                        LiquEx_size = 8 * hlq.xverts * hlq.yverts;
                        LiquEx      = new WMOLiquidVert[hlq.xverts * hlq.yverts];
                        for (var i = 0; i < hlq.xverts * hlq.yverts; ++i)
                        {
                            LiquEx[i] = reader.Read <WMOLiquidVert>();
                        }

                        int nLiquBytes = hlq.xtiles * hlq.ytiles;
                        LiquBytes = reader.ReadBytes(nLiquBytes);

                        // Determine legacy liquid type
                        if (groupLiquid == 0)
                        {
                            for (int i = 0; i < hlq.xtiles * hlq.ytiles; ++i)
                            {
                                if ((LiquBytes[i] & 0xF) != 15)
                                {
                                    groupLiquid = GetLiquidTypeId((uint)(LiquBytes[i] & 0xF) + 1);
                                    break;
                                }
                            }
                        }

                        /* std::ofstream llog("Buildings/liquid.log", ios_base::out | ios_base::app);
                         * llog << filename;
                         * llog << "\nbbox: " << bbcorn1[0] << ", " << bbcorn1[1] << ", " << bbcorn1[2] << " | " << bbcorn2[0] << ", " << bbcorn2[1] << ", " << bbcorn2[2];
                         * llog << "\nlpos: " << hlq->pos_x << ", " << hlq->pos_y << ", " << hlq->pos_z;
                         * llog << "\nx-/yvert: " << hlq->xverts << "/" << hlq->yverts << " size: " << size << " expected size: " << 30 + hlq->xverts*hlq->yverts*8 + hlq->xtiles*hlq->ytiles << std::endl;
                         * llog.close(); */
                    }
                    reader.BaseStream.Seek((int)nextpos, SeekOrigin.Begin);
                }
            }

            return(true);
        }
Esempio n. 4
0
        public static bool ExtractSingleWmo(string fileName)
        {
            // Copy files from archive
            string plainName = fileName.GetPlainName();

            if (File.Exists(Program.WmoDirectory + plainName))
            {
                return(true);
            }

            int p = 0;
            // Select root wmo files
            int index = plainName.IndexOf('_');

            if (index != -1)
            {
                string rchr = plainName.Substring(index);
                if (rchr != null)
                {
                    for (int i = 0; i < 4; ++i)
                    {
                        char m = rchr[i];
                        if (char.IsDigit(m))
                        {
                            p++;
                        }
                    }
                }
            }

            if (p == 3)
            {
                return(true);
            }

            bool file_ok = true;

            Console.WriteLine($"Extracting {fileName}");
            WMORoot froot = new WMORoot();

            if (!froot.open(fileName))
            {
                Console.WriteLine("Couldn't open RootWmo!!!");
                return(true);
            }

            using (BinaryWriter binaryWriter = new BinaryWriter(File.Open(Program.WmoDirectory + plainName, FileMode.Create, FileAccess.Write)))
            {
                froot.ConvertToVMAPRootWmo(binaryWriter);

                WmoDoodads[plainName] = froot.DoodadData;
                WMODoodadData doodads       = WmoDoodads[plainName];
                int           Wmo_nVertices = 0;
                for (int i = 0; i < froot.groupFileDataIDs.Count; ++i)
                {
                    WMOGroup fgroup = new WMOGroup();
                    if (!fgroup.open(froot.groupFileDataIDs[i], froot))
                    {
                        Console.WriteLine($"Could not open all Group file for: {plainName}");
                        file_ok = false;
                        break;
                    }

                    Wmo_nVertices += fgroup.ConvertToVMAPGroupWmo(binaryWriter, false);
                    foreach (ushort groupReference in fgroup.DoodadReferences)
                    {
                        if (groupReference >= doodads.Spawns.Count)
                        {
                            continue;
                        }

                        uint doodadNameIndex = doodads.Spawns[groupReference].NameIndex;
                        if (!froot.ValidDoodadNames.Contains(doodadNameIndex))
                        {
                            continue;
                        }

                        doodads.References.Add(groupReference);
                    }
                }

                binaryWriter.Seek(8, SeekOrigin.Begin); // store the correct no of vertices
                binaryWriter.Write(Wmo_nVertices);

                // Delete the extracted file in the case of an error
                if (!file_ok)
                {
                    File.Delete(Program.WmoDirectory + fileName);
                }
            }

            return(true);
        }
Esempio n. 5
0
        public bool init(uint map_num, uint originalMapId)
        {
            if (dirfileCache != null)
            {
                return(initFromCache(map_num, originalMapId));
            }

            MemoryStream stream = Program.cascHandler.ReadFile(Adtfilename);

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

            if (cacheable)
            {
                dirfileCache = new List <ADTOutputCache>();
            }

            string dirname = Program.wmoDirectory + "dir_bin";

            using (BinaryWriter binaryWriter = new BinaryWriter(File.Open(dirname, FileMode.Append, FileAccess.Write)))
            {
                using (BinaryReader binaryReader = new BinaryReader(stream))
                {
                    long fileLength = binaryReader.BaseStream.Length;
                    while (binaryReader.BaseStream.Position < fileLength)
                    {
                        string fourcc = binaryReader.ReadStringFromChars(4, true);
                        uint   size   = binaryReader.ReadUInt32();

                        long nextpos = binaryReader.BaseStream.Position + size;

                        if (fourcc == "MCIN")
                        {
                        }
                        else if (fourcc == "MTEX")
                        {
                        }
                        else if (fourcc == "MMDX")
                        {
                            if (size != 0)
                            {
                                while (size > 0)
                                {
                                    string path = binaryReader.ReadCString();

                                    ModelInstanceNames.Add(path.GetPlainName());
                                    VmapFile.ExtractSingleModel(path);

                                    size -= (uint)(path.Length + 1);
                                }
                            }
                        }
                        else if (fourcc == "MWMO")
                        {
                            if (size != 0)
                            {
                                while (size > 0)
                                {
                                    string path = binaryReader.ReadCString();

                                    WmoInstanceNames.Add(path.GetPlainName());
                                    VmapFile.ExtractSingleWmo(path);

                                    size -= (uint)(path.Length + 1);
                                }
                            }
                        }
                        //======================
                        else if (fourcc == "MDDF")
                        {
                            if (size != 0)
                            {
                                uint doodadCount = size / 36; //sizeof(MDDF)
                                for (int i = 0; i < doodadCount; ++i)
                                {
                                    MDDF doodadDef = binaryReader.Read <MDDF>();
                                    if (!Convert.ToBoolean(doodadDef.Flags & 0x40))
                                    {
                                        Model.Extract(doodadDef, ModelInstanceNames[(int)doodadDef.Id], map_num, originalMapId, binaryWriter, dirfileCache);
                                    }
                                    else
                                    {
                                        string fileName = $"FILE{doodadDef.Id}:X8.xxx";
                                        VmapFile.ExtractSingleModel(fileName);
                                        Model.Extract(doodadDef, fileName, map_num, originalMapId, binaryWriter, dirfileCache);
                                    }
                                }

                                ModelInstanceNames.Clear();
                            }
                        }
                        else if (fourcc == "MODF")
                        {
                            if (size != 0)
                            {
                                uint mapObjectCount = size / 64; // sizeof(ADT::MODF);
                                for (int i = 0; i < mapObjectCount; ++i)
                                {
                                    MODF mapObjDef = binaryReader.Read <MODF>();
                                    if (!Convert.ToBoolean(mapObjDef.Flags & 0x8))
                                    {
                                        WMORoot.Extract(mapObjDef, WmoInstanceNames[(int)mapObjDef.Id], false, map_num, originalMapId, binaryWriter, dirfileCache);
                                        Model.ExtractSet(VmapFile.WmoDoodads[WmoInstanceNames[(int)mapObjDef.Id]], mapObjDef, false, map_num, originalMapId, binaryWriter, dirfileCache);
                                    }
                                    else
                                    {
                                        string fileName = $"FILE{mapObjDef.Id}:8X.xxx";
                                        VmapFile.ExtractSingleWmo(fileName);
                                        WMORoot.Extract(mapObjDef, fileName, false, map_num, originalMapId, binaryWriter, dirfileCache);
                                        Model.ExtractSet(VmapFile.WmoDoodads[fileName], mapObjDef, false, map_num, originalMapId, binaryWriter, dirfileCache);
                                    }
                                }

                                WmoInstanceNames.Clear();
                            }
                        }

                        //======================
                        binaryReader.BaseStream.Seek(nextpos, SeekOrigin.Begin);
                    }
                }
            }

            return(true);
        }
Esempio n. 6
0
        public static bool ExtractSingleWmo(string fileName)
        {
            // Copy files from archive
            string plainName = fileName.GetPlainName();

            if (File.Exists(Program.wmoDirectory + plainName))
            {
                return(true);
            }

            int p = 0;
            // Select root wmo files
            int index = plainName.IndexOf('_');

            if (index != -1)
            {
                string rchr = plainName.Substring(index);
                if (rchr != null)
                {
                    for (int i = 0; i < 4; ++i)
                    {
                        char m = rchr[i];
                        if (char.IsDigit(m))
                        {
                            p++;
                        }
                    }
                }
            }

            if (p == 3)
            {
                return(true);
            }

            bool file_ok = true;

            Console.WriteLine($"Extracting {fileName}");
            WMORoot froot = new WMORoot();

            if (!froot.open(fileName))
            {
                Console.WriteLine("Couldn't open RootWmo!!!");
                return(true);
            }

            using (BinaryWriter binaryWriter = new BinaryWriter(File.Open(Program.wmoDirectory + plainName, FileMode.Create, FileAccess.Write)))
            {
                froot.ConvertToVMAPRootWmo(binaryWriter);
                int Wmo_nVertices = 0;
                //printf("root has %d groups\n", froot->nGroups);
                for (int i = 0; i < froot.groupFileDataIDs.Count; ++i)
                {
                    WMOGroup fgroup = new WMOGroup();
                    if (!fgroup.open(froot.groupFileDataIDs[i]))
                    {
                        Console.WriteLine($"Could not open all Group file for: {plainName}");
                        file_ok = false;
                        break;
                    }

                    Wmo_nVertices += fgroup.ConvertToVMAPGroupWmo(binaryWriter, froot, false);// preciseVectorData);
                }

                binaryWriter.Seek(8, SeekOrigin.Begin); // store the correct no of vertices
                binaryWriter.Write(Wmo_nVertices);

                // Delete the extracted file in the case of an error
                if (!file_ok)
                {
                    File.Delete(Program.wmoDirectory + fileName);
                }
            }

            return(true);
        }
Esempio n. 7
0
        public bool init(uint mapId)
        {
            Stream stream = Program.CascHandler.OpenFile(_mapName + ".wdt");

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

            string dirname = Program.WmoDirectory + "dir_bin";

            using (BinaryWriter binaryWriter = new BinaryWriter(File.Open(dirname, FileMode.Append, FileAccess.Write)))
            {
                using (BinaryReader binaryReader = new BinaryReader(stream))
                {
                    long fileLength = binaryReader.BaseStream.Length;
                    while (binaryReader.BaseStream.Position < fileLength)
                    {
                        string fourcc = binaryReader.ReadStringFromChars(4, true);
                        uint   size   = binaryReader.ReadUInt32();

                        long nextpos = binaryReader.BaseStream.Position + size;

                        if (fourcc == "MAIN")
                        {
                        }
                        if (fourcc == "MWMO")
                        {
                            // global map objects
                            if (size != 0)
                            {
                                while (size > 0)
                                {
                                    string path = binaryReader.ReadCString();

                                    _wmoNames.Add(path.GetPlainName());
                                    VmapFile.ExtractSingleWmo(path);

                                    size -= (uint)(path.Length + 1);
                                }
                            }
                        }
                        else if (fourcc == "MODF")
                        {
                            // global wmo instance data
                            if (size != 0)
                            {
                                uint mapObjectCount = size / 64; //sizeof(ADT::MODF);
                                for (int i = 0; i < mapObjectCount; ++i)
                                {
                                    MODF mapObjDef = binaryReader.Read <MODF>();
                                    if (!Convert.ToBoolean(mapObjDef.Flags & 0x8))
                                    {
                                        WMORoot.Extract(mapObjDef, _wmoNames[(int)mapObjDef.Id], true, mapId, mapId, binaryWriter, null);
                                        Model.ExtractSet(VmapFile.WmoDoodads[_wmoNames[(int)mapObjDef.Id]], mapObjDef, true, mapId, mapId, binaryWriter, null);
                                    }
                                    else
                                    {
                                        string fileName = $"FILE{mapObjDef.Id}:8X.xxx";
                                        VmapFile.ExtractSingleWmo(fileName);
                                        WMORoot.Extract(mapObjDef, fileName, true, mapId, mapId, binaryWriter, null);
                                        Model.ExtractSet(VmapFile.WmoDoodads[fileName], mapObjDef, true, mapId, mapId, binaryWriter, null);
                                    }
                                }
                            }
                        }

                        binaryReader.BaseStream.Seek(nextpos, SeekOrigin.Begin);
                    }
                }
            }

            return(true);
        }
Esempio n. 8
0
        public bool Init(uint mapNum, uint originalMapId)
        {
            if (dirFileCache != null)
            {
                return(InitFromCache(mapNum, originalMapId));
            }

            if (cacheable)
            {
                dirFileCache = new List <ADTOutputCache>();
            }

            FilenameChunk mmdx = GetChunk("MMDX")?.As <FilenameChunk>();

            if (mmdx != null && mmdx.Filenames.Count > 0)
            {
                foreach (var filename in mmdx.Filenames)
                {
                    modelInstanceNames.Add(filename);
                    VmapFile.ExtractSingleModel(filename);
                }
            }

            FilenameChunk mwmo = GetChunk("MWMO")?.As <FilenameChunk>();

            if (mwmo != null && mwmo.Filenames.Count > 0)
            {
                foreach (var filename in mwmo.Filenames)
                {
                    wmoInstanceNames.Add(filename);
                    VmapFile.ExtractSingleWmo(filename);
                }
            }

            MDDF doodadChunk = GetChunk("MDDF")?.As <MDDF>();

            if (doodadChunk != null && doodadChunk.DoodadDefs.Length > 0)
            {
                foreach (var doodad in doodadChunk.DoodadDefs)
                {
                    if (doodad.Flags.HasAnyFlag(MDDFFlags.EntryIsFileID))
                    {
                        string fileName = $"FILE{doodad.Id:X8}.xxx";
                        VmapFile.ExtractSingleModel(fileName);
                        Model.Extract(doodad, fileName, mapNum, originalMapId, Program.DirBinWriter, dirFileCache);
                    }
                    else
                    {
                        Model.Extract(doodad, modelInstanceNames[(int)doodad.Id], mapNum, originalMapId, Program.DirBinWriter, dirFileCache);
                    }
                }

                modelInstanceNames.Clear();
            }

            MODF wmoChunk = GetChunk("MODF")?.As <MODF>();

            if (wmoChunk != null && wmoChunk.MapObjDefs.Length > 0)
            {
                foreach (var wmo in wmoChunk.MapObjDefs)
                {
                    if (wmo.Flags.HasAnyFlag(MODFFlags.EntryIsFileID))
                    {
                        string fileName = $"FILE{wmo.Id:X8}.xxx";
                        VmapFile.ExtractSingleWmo(wmo.Id);
                        WMORoot.Extract(wmo, fileName, false, mapNum, originalMapId, Program.DirBinWriter, dirFileCache);

                        if (VmapFile.WmoDoodads.ContainsKey(fileName))
                        {
                            Model.ExtractSet(VmapFile.WmoDoodads[fileName], wmo, false, mapNum, originalMapId, Program.DirBinWriter, dirFileCache);
                        }
                    }
                    else
                    {
                        WMORoot.Extract(wmo, wmoInstanceNames[(int)wmo.Id], false, mapNum, originalMapId, Program.DirBinWriter, dirFileCache);
                        if (VmapFile.WmoDoodads.ContainsKey(wmoInstanceNames[(int)wmo.Id]))
                        {
                            Model.ExtractSet(VmapFile.WmoDoodads[wmoInstanceNames[(int)wmo.Id]], wmo, false, mapNum, originalMapId, Program.DirBinWriter, dirFileCache);
                        }
                    }
                }

                wmoInstanceNames.Clear();
            }

            return(true);
        }
Esempio n. 9
0
        public int ConvertToVMAPGroupWmo(BinaryWriter writer, WMORoot rootWMO, bool preciseVectorData)
        {
            writer.Write(mogpFlags);
            writer.Write(groupWMOID);
            // group bound
            for (var i = 0; i < 3; ++i)
            {
                writer.Write(bbcorn1[i]);
            }
            for (var i = 0; i < 3; ++i)
            {
                writer.Write(bbcorn2[i]);
            }
            writer.Write(liquflags);
            int nColTriangles = 0;

            if (preciseVectorData)
            {
                writer.WriteString("GRP ");

                int k          = 0;
                int moba_batch = moba_size / 12;
                MobaEx = new int[moba_batch * 4];
                for (int i = 8; i < moba_size; i += 12)
                {
                    MobaEx[k++] = MOBA[i];
                }
                int moba_size_grp = moba_batch * 4 + 4;
                writer.Write(moba_size_grp);
                writer.Write(moba_batch);
                for (var i = 0; i < k; ++i)
                {
                    writer.Write(MobaEx[i]);
                }

                int nIdexes = nTriangles * 3;

                writer.WriteString("INDX");
                int wsize = 4 + 2 * nIdexes;
                writer.Write(wsize);
                writer.Write(nIdexes);
                if (nIdexes > 0)
                {
                    for (var i = 0; i < nIdexes; ++i)
                    {
                        writer.Write(MOVI[i]);
                    }
                }

                writer.WriteString("VERT");
                wsize = (int)(4 + 4 * 3 * nVertices);
                writer.Write(wsize);
                writer.Write(nVertices);
                if (nVertices > 0)
                {
                    for (var i = 0; i < nVertices; ++i)//May need nVertices * 3
                    {
                        writer.Write(MOVT[i]);
                    }
                }

                nColTriangles = nTriangles;
            }
            else
            {
                writer.WriteString("GRP ");
                int k          = 0;
                int moba_batch = moba_size / 12;
                MobaEx = new int[moba_batch * 4];
                for (int i = 8; i < moba_size; i += 12)
                {
                    MobaEx[k++] = MOBA[i];
                }

                int moba_size_grp = moba_batch * 4 + 4;
                writer.Write(moba_size_grp);
                writer.Write(moba_batch);
                for (var i = 0; i < k; ++i)
                {
                    writer.Write(MobaEx[i]);
                }

                //-------INDX------------------------------------
                //-------MOPY--------
                MoviEx = new ushort[nTriangles * 3]; // "worst case" size...
                int[] IndexRenum = new int[nVertices];
                for (var i = 0; i < nVertices; ++i)
                {
                    IndexRenum[i] = 0xFF;
                }
                for (int i = 0; i < nTriangles; ++i)
                {
                    // Skip no collision triangles
                    bool isRenderFace = Convert.ToBoolean(MOPY[2 * i] & (int)MopyFlags.Render) && !Convert.ToBoolean(MOPY[2 * i] & (int)MopyFlags.Detail);
                    bool isDetail     = (MOPY[2 * i] & (int)MopyFlags.Detail) != 0;
                    bool isCollision  = (MOPY[2 * i] & (int)MopyFlags.Collision) != 0;

                    if (!isRenderFace && !isDetail && !isCollision)
                    {
                        continue;
                    }

                    // Use this triangle
                    for (int j = 0; j < 3; ++j)
                    {
                        IndexRenum[MOVI[3 * i + j]]   = 1;
                        MoviEx[3 * nColTriangles + j] = MOVI[3 * i + j];
                    }
                    ++nColTriangles;
                }

                // assign new vertex index numbers
                int nColVertices = 0;
                for (uint i = 0; i < nVertices; ++i)
                {
                    if (IndexRenum[i] == 1)
                    {
                        IndexRenum[i] = nColVertices;
                        ++nColVertices;
                    }
                }

                // translate triangle indices to new numbers
                for (int i = 0; i < 3 * nColTriangles; ++i)
                {
                    //assert(MoviEx[i] < nVertices);
                    MoviEx[i] = (ushort)IndexRenum[MoviEx[i]];
                }

                // write triangle indices
                writer.Write(0x58444E49);
                writer.Write(nColTriangles * 6 + 4);
                writer.Write(nColTriangles * 3);

                for (var i = 0; i < nColTriangles * 3; ++i)
                {
                    writer.Write(MoviEx[i]);
                }

                // write vertices
                int check = 3 * nColVertices;
                writer.Write(0x54524556);
                writer.Write(nColVertices * 3 * 4 + 4);
                writer.Write(nColVertices);
                for (uint i = 0; i < nVertices; ++i)
                {
                    if (IndexRenum[i] >= 0)
                    {
                        writer.Write(MOVT[3 * i]);
                        writer.Write(MOVT[3 * i + 1]);
                        writer.Write(MOVT[3 * i + 2]);
                        check -= 4;
                    }
                }

                //assert(check == 0);
            }

            //------LIQU------------------------
            if (LiquEx_size != 0)
            {
                writer.Write(0x5551494C);
                writer.Write((32 + LiquEx_size) + hlq.xtiles * hlq.ytiles);

                // according to WoW.Dev Wiki:
                uint liquidEntry;
                if (Convert.ToBoolean(rootWMO.flags & 4))
                {
                    liquidEntry = liquidType;
                }
                else if (liquidType == 15)
                {
                    liquidEntry = 0;
                }
                else
                {
                    liquidEntry = liquidType + 1;
                }

                if (liquidEntry == 0)
                {
                    int v1; // edx@1
                    int v2; // eax@1

                    v1 = hlq.xtiles * hlq.ytiles;
                    v2 = 0;
                    if (v1 > 0)
                    {
                        while ((LiquBytes[v2] & 0xF) == 15)
                        {
                            ++v2;
                            if (v2 >= v1)
                            {
                                break;
                            }
                        }

                        if (v2 < v1 && (LiquBytes[v2] & 0xF) != 15)
                        {
                            liquidEntry = (LiquBytes[v2] & 0xFu) + 1;
                        }
                    }
                }

                if (liquidEntry != 0 && liquidEntry < 21)
                {
                    switch ((liquidEntry - 1) & 3)
                    {
                    case 0:
                        liquidEntry = ((mogpFlags & 0x80000) != 0) ? 1 : 0 + 13u;
                        break;

                    case 1:
                        liquidEntry = 14;
                        break;

                    case 2:
                        liquidEntry = 19;
                        break;

                    case 3:
                        liquidEntry = 20;
                        break;
                    }
                }

                hlq.type = (short)liquidEntry;

                /* std::ofstream llog("Buildings/liquid.log", ios_base::out | ios_base::app);
                 * llog << filename;
                 * llog << ":\nliquidEntry: " << liquidEntry << " type: " << hlq->type << " (root:" << rootWMO->flags << " group:" << flags << ")\n";
                 * llog.close(); */

                writer.WriteStruct(hlq);
                // only need height values, the other values are unknown anyway
                for (uint i = 0; i < LiquEx_size / 8; ++i)
                {
                    writer.Write(LiquEx[i].height);
                }
                // todo: compress to bit field
                writer.Write(LiquBytes, 0, hlq.xtiles * hlq.ytiles);
            }

            return(nColTriangles);
        }