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); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }