Esempio n. 1
0
        private float[] ReadHeights(MH2O currentMH2O, uint ofsData2b, int HeightMapLen, long offset)
        {
            var heights = new float[HeightMapLen];

            if (ofsData2b != 0)
            {
                Reader.BaseStream.Position = offset;
                for (int i = 0; i < HeightMapLen; i++)
                {
                    var height = Reader.ReadSingle();
                    if (height == 0)
                    {
                        height = currentMH2O.heightLevel1;
                    }
                    heights[i] = height;
                }
            }
            else
            {
                for (int i = 0; i < HeightMapLen; i++)
                {
                    heights[i] = currentMH2O.heightLevel1;
                }
            }
            return(heights);
        }
Esempio n. 2
0
        /// <summary>
        /// Processes an individual MH2O Chunk
        /// </summary>
        /// <param name="Header">MH20 Header Chunk</param>
        /// <param name="ofsMH20">MH20 OFFset</param>
        /// <returns>MH2O Filled with information</returns>
        private MH2O processMH20(MH2OHeader Header, long ofsMH20)
        {
            if (Header.used == 0)
            {
                return(new MH2O {
                    used = false
                });
            }

            Reader.BaseStream.Position = ofsMH20 + Header.ofsData1 + 2;

            var currentMH2O = new MH2O();

            currentMH2O.used         = true;
            currentMH2O.type         = ((MH2O.FluidType)Reader.ReadUInt16());
            currentMH2O.heightLevel1 = Reader.ReadSingle();
            currentMH2O.heightLevel2 = Reader.ReadSingle();
            currentMH2O.xOffset      = Reader.ReadByte();
            currentMH2O.yOffset      = Reader.ReadByte();
            currentMH2O.width        = Reader.ReadByte();
            currentMH2O.height       = Reader.ReadByte();

            UInt32 ofsData2a = Reader.ReadUInt32();
            UInt32 ofsData2b = Reader.ReadUInt32();


            currentMH2O.RenderBitMap = ReadRenderBitMap(currentMH2O, ofsData2a, ofsMH20 + ofsData2a, ofsData2b - ofsData2a);

            currentMH2O.heights = ReadHeights(currentMH2O, ofsData2b, (currentMH2O.width + 1) * (currentMH2O.height + 1), ofsMH20 + ofsData2b);

            return(currentMH2O);
        }
Esempio n. 3
0
        private MH2O ReadMH20SubChunk(uint size, BinaryReader bin)
        {
            var chunk = new MH2O();

            chunk.headers = new MH2OHeader[256];
            for (var i = 0; i < 256; i++)
            {
                chunk.headers[i].offsetInstances  = bin.ReadUInt32();
                chunk.headers[i].layerCount       = bin.ReadUInt32();
                chunk.headers[i].offsetAttributes = bin.ReadUInt32();
            }
            return(chunk);
        }
Esempio n. 4
0
        public void Read()
        {
            if (Chunk == null)
            {
                return;
            }

            var stream = Chunk.GetStream();

            MH2O = new MH2O();
            MH2O.Read(Chunk.GetReader());
            HeightMaps  = new MH2O.MH2OHeightmapData[256];
            Information = new MH2O.MH2OInformation[256];

            for (int i = 0; i < MH2O.Headers.Length; i++)
            {
                var header = MH2O.Headers[i];
                if (header == null || header.LayerCount == 0)
                {
                    continue;
                }

                stream.Seek(Chunk.Offset + header.ofsInformation, SeekOrigin.Begin);
                var information = new MH2O.MH2OInformation();
                information.Read(Chunk.GetReader());
                Information[i] = information;

                // Ensure we have heightmap data
                if (information.ofsHeightmapData == 0)
                {
                    continue;
                }

                MH2O.MH2OHeightmapData heightMap;
                if (IsOcean(information.LiquidObjectId, information.LiquidTypeId))
                {
                    heightMap = GetOceanHeightMap(information.MinHeightLevel);
                }
                else
                {
                    // Read the height map
                    stream.Seek(Chunk.Offset + information.ofsHeightmapData, SeekOrigin.Begin);
                    heightMap = new MH2O.MH2OHeightmapData();
                    heightMap.Read(Chunk.GetReader());
                    heightMap.RenderMask = GetRenderMask(header, information);
                }

                HeightMaps[i] = heightMap;
            }
        }
Esempio n. 5
0
        private byte[] ReadRenderBitMap(MH2O currentMH2O, uint ofsData2a, long offset, uint size)
        {
            var map = new byte[currentMH2O.height];

            if (ofsData2a != 0)
            {
                Reader.BaseStream.Position = offset;
                for (int i = 0; i < currentMH2O.height; i++)
                {
                    map[i] = i < size?Reader.ReadByte() : (byte)0x00;
                }
            }
            return(map);
        }
Esempio n. 6
0
        public ADT(Files.WOTLK.ADT wotlk)
        {
            ADTfileInfo = new FileInfo(wotlk.ADTfileInfo.Name.Split('.')[0] + ".adt");
            Logger.log(ADTfileInfo.Name, Logger.Type.CONVERT, "<- " + wotlk.ADTfileInfo.Name);


            MVER = new MVER(wotlk.MVER);
            MHDR = new MHDR(wotlk.MHDR);

            if (wotlk.MH2O != null)
            {
                MH2O = new MH2O(wotlk.MH2O);
            }

            foreach (Files.WOTLK.Chunks.MCNK x in wotlk.MCNKs)
            {
                MCNKs.Add(new MCNK_ADT(x));
                MCNKLength += MCNKs[MCNKs.Count - 1].GetBytes().Length;
            }
            Logger.log("MCNK[]", Logger.Type.LEVEL1);

            if (wotlk.MFBO != null)
            {
                MFBO = new MFBO(wotlk.MFBO);
            }
            else if (YetAnotherAdtConverter.Program.config.CreateMFBO)
            {
                UInt32 address = 0;
                MFBO = new MFBO();

                address += (UInt32)MHDR.Header.Byte_size;

                if (MH2O != null)
                {
                    address += (UInt32)MH2O.GetBytes().Length;
                }

                address += (UInt32)MCNKLength;
                MHDR.UpdateMFBO(address);
            }
        }
Esempio n. 7
0
        private static void PrepareWaterInfo(MH2O water)
        {
            if (water == null)
            {
                return;
            }
            if (!water.Header.Used)
            {
                return;
            }

            var min = float.MaxValue;
            var max = float.MinValue;

            foreach (var height in water.Heights)
            {
                min = Math.Min(height, min);
                max = Math.Max(height, max);
            }
            water.IsFlat = (Math.Abs(max - min) < 1.0f);
        }
Esempio n. 8
0
        /// <summary>
        /// Parse all MH2O element from file stream
        /// </summary>
        public override MH2O[,] Parse()
        {
            Reader.BaseStream.Position = AbsoluteStart;
            var result     = new MH2O[16, 16];
            var mh2oHeader = new MH2OHeader[256];

            for (int i = 0; i < 256; i++)
            {
                mh2oHeader[i].ofsData1 = Reader.ReadUInt32();
                mh2oHeader[i].used     = Reader.ReadUInt32();
                mh2oHeader[i].ofsData2 = Reader.ReadUInt32();
            }
            for (int y = 0; y < 16; y++)
            {
                for (int x = 0; x < 16; x++)
                {
                    result[x, y] = processMH20(mh2oHeader[y * 16 + x], AbsoluteStart);
                }
            }
            return(result);
        }
Esempio n. 9
0
        public ADT(string filePath)
        {
            ADTfileInfo = new FileInfo(filePath);

            Logger.log(ADTfileInfo.Name, Logger.Type.READ, ADTfileInfo.DirectoryName);

            //Start reading the .ADT File
            using (BinaryReader reader = new BinaryReader(ADTfileInfo.Open(FileMode.Open)))
            {
                Length = (int)reader.BaseStream.Length;
                reader.BaseStream.Seek(0, SeekOrigin.Begin);
                int MCNK_counter = 0;
                int MCNK_size    = 0;

                while (reader.BaseStream.Position < reader.BaseStream.Length)
                {
                    byte[] ChunkMagic   = reader.ReadBytes(4);
                    byte[] ChunkSize    = reader.ReadBytes(4);
                    byte[] ChunkContent = reader.ReadBytes(BitConverter.ToInt32(ChunkSize, 0));

                    string ChunkMagicString = MagicBytesToString(ChunkMagic);
                    //read the chunks
                    switch (ChunkMagicString)
                    {
                    case "MVER":
                        MVER = new MVER(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent);
                        break;

                    case "MHDR":
                        MHDR = new MHDR(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent);
                        break;

                    case "MCIN":
                        MCIN = new MCIN(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent);
                        break;

                    case "MTEX":
                        MTEX = new MTEX(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent);
                        break;

                    case "MMDX":
                        MMDX = new MMDX(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent);
                        break;

                    case "MMID":
                        MMID = new MMID(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent);
                        break;

                    case "MWMO":
                        MWMO = new MWMO(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent);
                        break;

                    case "MWID":
                        MWID = new MWID(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent);
                        break;

                    case "MDDF":
                        MDDF = new MDDF(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent);
                        break;

                    case "MODF":
                        MODF = new MODF(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent);
                        break;

                    case "MH2O":
                        MH2O = new MH2O(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent);
                        break;

                    case "MCNK":
                        MCNKs.Add(new MCNK(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent));
                        break;

                    case "MFBO":
                        MFBO = new MFBO(MagicBytesToChars(ChunkMagic), ChunkSize, ChunkContent);
                        break;
                    }

                    if (ChunkMagicString == "MCNK")
                    {
                        MCNK_counter++; MCNK_size += BitConverter.ToInt32(ChunkSize, 0);
                    }
                    else if (ChunkMagicString == "\0\0\0\0") /*Logger.log("0 Byte Chunk", Logger.Direction.WARNING);*/ } {
            }

            if (MCNK_counter > 0)
            {
                Logger.log("MCNK[]", Logger.Type.LEVEL1, MCNK_size + " byte");
            }
        }
    }
Esempio n. 10
0
	    private static void PrepareWaterInfo(MH2O water)
        {
            if (water == null) return;
            if (!water.Header.Used) return;

            var min = float.MaxValue;
            var max = float.MinValue;
            foreach (var height in water.Heights)
            {
                min = Math.Min(height, min);
                max = Math.Max(height, max);
            }
            water.IsFlat = (Math.Abs(max - min) < TerrainConstants.MinAllowedHeightDiff);
        }
Esempio n. 11
0
        public void Read()
        {
            if (Chunk == null)
            {
                return;
            }

            Vertices = new List <Vector3>();
            Indices  = new List <Triangle <uint> >();

            var stream = Chunk.GetStream();

            MH2O = new MH2O();
            MH2O.Read(Chunk.GetReader());
            HeightMaps = new Chunks.MH2O.MH2OHeightmapData[256];

            var tilePos = ADT.TilePosition;

            for (int i = 0; i < MH2O.Headers.Length; i++)
            {
                var header = MH2O.Headers[i];
                if (header == null || header.LayerCount == 0)
                {
                    continue;
                }

                stream.Seek(Chunk.Offset + header.ofsInformation, SeekOrigin.Begin);
                var information = new MH2O.MH2OInformation();
                information.Read(Chunk.GetReader());

                // Ensure we have heightmap data
                if (information.ofsHeightmapData == 0)
                {
                    continue;
                }

                // Not an ocean, lets grab the height map and render mask
                MH2O.MH2OHeightmapData heightMap;
                if (!IsOcean(information.LiquidObjectId, information.LiquidTypeId))
                { // Read the height map
                    stream.Seek(Chunk.Offset + information.ofsHeightmapData, SeekOrigin.Begin);
                    heightMap = new MH2O.MH2OHeightmapData();
                    heightMap.Read(Chunk.GetReader());
                    heightMap.RenderMask = GetRenderMask(header, information);
                }
                else
                {
                    heightMap = GetOceanHeightMap(information.MinHeightLevel);
                }

                HeightMaps[i] = heightMap;

                var basePos = new Vector3(tilePos.X - (Constants.ChunkSize * (i % 16)), tilePos.Y - (Constants.ChunkSize * (i / 16)), 0);
                for (int y = information.YOffset; y < (information.YOffset + information.Height); y++)
                {
                    for (int x = information.XOffset; x < (information.XOffset + information.Width); x++)
                    {
                        if (!heightMap.RenderMask.ShouldRender(x, y))
                        {
                            continue;
                        }

                        var v  = new Vector3(basePos.X - (x * Constants.UnitSize), basePos.Y - (y * Constants.UnitSize), heightMap.Heightmap[x, y]);
                        var vo = (uint)Vertices.Count;
                        Vertices.Add(v);
                        Vertices.Add(new Vector3(v.X - Constants.UnitSize, v.Y, v.Z));
                        Vertices.Add(new Vector3(v.X, v.Y - Constants.UnitSize, v.Z));
                        Vertices.Add(new Vector3(v.X - Constants.UnitSize, v.Y - Constants.UnitSize, v.Z));

                        Indices.Add(new Triangle <uint>(TriangleType.Water, vo, vo + 2, vo + 1));
                        Indices.Add(new Triangle <uint>(TriangleType.Water, vo + 2, vo + 3, vo + 1));
                    }
                }
            }
        }
Esempio n. 12
0
		private static MH2O ProcessMH2O(BinaryReader fileReader, MH2OHeader header, long waterSegmentBase)
		{
			var water = new MH2O();

			if (header.LayerCount == 0)
			{
				water.Used = false;
				return water;
			}

			water.Used = true;

			fileReader.BaseStream.Position = waterSegmentBase + header.ofsData1;

			water.Flags = (MH2OFlags)fileReader.ReadUInt16();

			water.Type = (FluidType)fileReader.ReadUInt16();
			water.HeightLevel1 = fileReader.ReadSingle();
			water.HeightLevel2 = fileReader.ReadSingle();
			water.XOffset = fileReader.ReadByte();
			water.YOffset = fileReader.ReadByte();
			water.Width = fileReader.ReadByte();
			water.Height = fileReader.ReadByte();

			var ofsWaterFlags = fileReader.ReadUInt32();
			var ofsWaterHeightMap = fileReader.ReadUInt32();

			var heightMapLen = (water.Width + 1) * (water.Height + 1);
			var heights = new float[heightMapLen];

			// If flags is 2, the chunk is for an ocean, and there is no heightmap
			if (ofsWaterHeightMap != 0 && (water.Flags & MH2OFlags.Ocean) == 0)
			{
				fileReader.BaseStream.Position = waterSegmentBase + ofsWaterHeightMap;

				for (var i = 0; i < heightMapLen; i++)
				{
					heights[i] = fileReader.ReadSingle();
					if (heights[i] == 0)
					{
						heights[i] = water.HeightLevel1;
					}
				}

			}
			else
			{
				for (var i = 0; i < heightMapLen; i++)
				{
					heights[i] = water.HeightLevel1;
				}
			}

			water.Heights = new float[water.Height + 1, water.Width + 1];
			for (var r = 0; r <= water.Height; r++)
			{
				for (var c = 0; c <= water.Width; c++)
				{

					water.Heights[r, c] = heights[c + r * c];
				}
			}

			return water;
		}
Esempio n. 13
0
        private static MH2O ProcessMH2O(BinaryReader fileReader, MH20Header header, long waterSegmentBase)
        {
            var water = new MH2O();

            if (header.LayerCount == 0)
            {
                water.Header.Used = false;
                return(water);
            }

            water.Header.Used = true;

            fileReader.BaseStream.Position = waterSegmentBase + header.ofsData1;

            water.Header.Flags = (MH2OFlags)fileReader.ReadUInt16();

            water.Header.Type         = (LiquidType)fileReader.ReadUInt16();
            water.Header.HeightLevel1 = fileReader.ReadSingle();
            water.Header.HeightLevel2 = fileReader.ReadSingle();
            water.Header.YOffset      = fileReader.ReadByte();
            water.Header.XOffset      = fileReader.ReadByte();
            water.Header.Width        = fileReader.ReadByte();
            water.Header.Height       = fileReader.ReadByte();

            var ofsWaterFlags     = fileReader.ReadUInt32();
            var ofsWaterHeightMap = fileReader.ReadUInt32();

            water.RenderBitMap = new byte[water.Header.Height];
            if (ofsWaterFlags != 0)
            {
                fileReader.BaseStream.Position = waterSegmentBase + ofsWaterFlags;
                for (var i = 0; i < water.Header.Height; i++)
                {
                    if (i < (ofsWaterHeightMap - ofsWaterFlags))
                    {
                        water.RenderBitMap[i] = fileReader.ReadByte();
                    }
                    else
                    {
                        water.RenderBitMap[i] = 0;
                    }
                }
            }


            //var heightMapLen = (water.Header.Width + 1) * (water.Header.Height + 1);
            var heightMapLen = (TerrainConstants.UnitsPerChunkSide + 1) * (TerrainConstants.UnitsPerChunkSide + 1);

            water.HeightsArray = new float[heightMapLen];

            // If flags is 2, the chunk is for an ocean, and there is no heightmap
            if (ofsWaterHeightMap != 0 && (water.Header.Flags & MH2OFlags.Ocean) == 0)
            {
                fileReader.BaseStream.Position = waterSegmentBase + ofsWaterHeightMap;
                for (var i = 0; i < heightMapLen; i++)
                {
                    water.HeightsArray[i] = fileReader.ReadSingle();
                    if (water.HeightsArray[i].IsWithinEpsilon(0.0f))
                    {
                        water.HeightsArray[i] = water.Header.HeightLevel1;
                    }
                }
            }
            else
            {
                for (var i = 0; i < heightMapLen; i++)
                {
                    water.HeightsArray[i] = water.Header.HeightLevel1;
                }
            }

            return(water);
        }
Esempio n. 14
0
File: ADT.cs Progetto: hazzik/mpqnav
        private TriangleList GenerateVertexAndIndicesH2O()
        {
            var vertices = new List <VertexPositionNormalColored>();
            var indices  = new List <int>();

            if (MH2OArray != null)
            {
                float offset_x = (533.33333f / 16) / 8;
                float offset_z = (533.33333f / 16) / 8;

                int VertexCounter      = 0;
                int _TempVertexCounter = 0;
                for (int My = 0; My < 16; My++)
                {
                    for (int Mx = 0; Mx < 16; Mx++)
                    {
                        float[,] MH2OHeightMap;
                        bool[,] MH2ORenderMap;

                        _TempVertexCounter = VertexCounter;
                        float x    = MCNKArray[Mx, My].x;
                        float z    = MCNKArray[Mx, My].z;
                        MH2O  mh2O = MH2OArray[Mx, My];

                        float y_pos = mh2O.heightLevel1;
                        if (mh2O.used)
                        {
                            Color clr = GetColor(mh2O.type);
                            MH2OHeightMap = mh2O.GetMapHeightsMatrix();
                            MH2ORenderMap = mh2O.GetRenderBitMapMatrix();
                            for (int r = 0; r < 9; r++)
                            {
                                for (int c = 0; c < 9; c++)
                                {
                                    float x_pos = x - (c * offset_x);
                                    float z_pos = z - (r * offset_z);
                                    if (((r >= mh2O.yOffset) && ((r - mh2O.yOffset) <= mh2O.height)) &&
                                        ((c >= mh2O.xOffset) && ((c - mh2O.xOffset) <= mh2O.width)))
                                    {
                                        y_pos = MH2OHeightMap[r - mh2O.yOffset, c - mh2O.xOffset];                                         // +_MH2O.heightLevel1;
                                        var position = new Vector3(x_pos, y_pos, z_pos);

                                        vertices.Add(new VertexPositionNormalColored(position, clr, Vector3.Up));
                                        _TempVertexCounter++;
                                    }
                                }
                            }

                            for (int r = mh2O.yOffset; r < mh2O.yOffset + mh2O.height; r++)
                            {
                                for (int c = mh2O.xOffset; c < mh2O.xOffset + mh2O.width; c++)
                                {
                                    int row = r - mh2O.yOffset;
                                    int col = c - mh2O.xOffset;

                                    //if ((MH2ORenderMap[row, c]) || ((_MH2O.height == 8) && (_MH2O.width == 8)))
                                    {
                                        indices.Add(VertexCounter + ((row + 1) * (mh2O.width + 1) + col));
                                        indices.Add(VertexCounter + (row * (mh2O.width + 1) + col));
                                        indices.Add(VertexCounter + (row * (mh2O.width + 1) + col + 1));
                                        indices.Add(VertexCounter + ((row + 1) * (mh2O.width + 1) + col + 1));
                                        indices.Add(VertexCounter + ((row + 1) * (mh2O.width + 1) + col));
                                        indices.Add(VertexCounter + (row * (mh2O.width + 1) + col + 1));
                                    }
                                }
                            }
                            VertexCounter = _TempVertexCounter;
                        }
                    }
                }
            }
            return(new TriangleList(indices, vertices));
        }
Esempio n. 15
0
        public void Write()
        {
            using (var writer = new BinaryWriter(File.OpenWrite("test.adt")))
            {
                // MVER
                writer.Write(MVER.GetChunkHeaderBytes());
                writer.Write(MVER.GetChunkBytes());

                // Write MHDR later when we got all offsets
                var positionBeforeMHDR = writer.BaseStream.Position;
                writer.BaseStream.Position += _HeaderSize + MHDR.ChunkSize;

                // MTEX
                MHDR.OffsetMTEX = (uint)writer.BaseStream.Position;
                writer.Write(MTEX.GetChunkHeaderBytes());
                writer.Write(MTEX.GetChunkBytes());

                // MMDX
                MHDR.OffsetMMDX = (uint)writer.BaseStream.Position;
                writer.Write(MMDX.GetChunkHeaderBytes());
                writer.Write(MMDX.GetChunkBytes());

                // MMID
                MHDR.OffsetMMID = (uint)writer.BaseStream.Position;
                writer.Write(MMID.GetChunkHeaderBytes());
                writer.Write(MMID.GetChunkBytes());

                // MWMO
                MHDR.OffsetMWMO = (uint)writer.BaseStream.Position;
                writer.Write(MWMO.GetChunkHeaderBytes());
                writer.Write(MWMO.GetChunkBytes());

                // MWID
                MHDR.OffsetMWID = (uint)writer.BaseStream.Position;
                writer.Write(MWID.GetChunkHeaderBytes());
                writer.Write(MWID.GetChunkBytes());

                // MDDF
                MHDR.OffsetMDDF = (uint)writer.BaseStream.Position;
                writer.Write(MDDF.GetChunkHeaderBytes());
                writer.Write(MDDF.GetChunkBytes());

                // MODF
                MHDR.OffsetMODF = (uint)writer.BaseStream.Position;
                writer.Write(MODF.GetChunkHeaderBytes());
                writer.Write(MODF.GetChunkBytes());

                // MH2O
                MHDR.OffsetMH2O = (uint)writer.BaseStream.Position;
                writer.Write(MH2O.GetChunkHeaderBytes());
                writer.Write(MH2O.GetChunkBytes());

                // MCNK
                for (int i = 0; i < MCIN.Entries.Length; i++)
                {
                    MCIN.Entries[i].OffsetMCNK = (uint)writer.BaseStream.Position;
                    MCIN.Entries[i].ChunkSize  = MCNK[i].ChunkSize;
                    writer.Write(MCNK[i].GetChunkHeaderBytes());
                    writer.Write(MCNK[i].GetChunkBytes());
                }

                // MCIN
                MHDR.OffsetMCIN = (uint)writer.BaseStream.Position;
                writer.Write(MCIN.GetChunkHeaderBytes());
                writer.Write(MCIN.GetChunkBytes());

                // MFBO
                if (MHDR.Flags.HasFlag(MHDRFlags.MFBO))
                {
                    MHDR.OffsetMFBO = (uint)writer.BaseStream.Position;
                    writer.Write(MFBO.GetChunkHeaderBytes());
                    writer.Write(MFBO.GetChunkBytes());
                }
                else
                {
                    MHDR.OffsetMFBO = 0;
                }

                // MTXF
                MHDR.OffsetMTXF = (uint)writer.BaseStream.Position;
                writer.Write(MTXF.GetChunkHeaderBytes());
                writer.Write(MTXF.GetChunkBytes());

                // MHDR
                writer.BaseStream.Position = positionBeforeMHDR;
                writer.Write(MHDR.GetChunkHeaderBytes());
                writer.Write(MHDR.GetChunkBytes());
            }
        }
Esempio n. 16
0
        public static bool ConvertADT(ChunkedFile adt, string mapName, string outputPath, int gx, int gy, uint build, bool ignoreDeepWater)
        {
            // Prepare map header
            MapFileHeader map;

            map.mapMagic     = SharedConst.MAP_MAGIC;
            map.versionMagic = SharedConst.MAP_VERSION_MAGIC;
            map.buildMagic   = build;

            // Get area flags data
            for (var x = 0; x < SharedConst.ADT_CELLS_PER_GRID; ++x)
            {
                for (var y = 0; y < SharedConst.ADT_CELLS_PER_GRID; ++y)
                {
                    AreaIDs[x][y]       = 0;
                    LiquidEntries[x][y] = 0;
                    LiquidFlags[x][y]   = 0;
                }
            }

            for (var x = 0; x < SharedConst.ADT_GRID_SIZE; ++x)
            {
                for (var y = 0; y < SharedConst.ADT_GRID_SIZE; ++y)
                {
                    V8[x][y]         = 0;
                    LiquidSnow[x][y] = false;
                }
            }

            for (var x = 0; x < SharedConst.ADT_GRID_SIZE + 1; ++x)
            {
                for (var y = 0; y < SharedConst.ADT_GRID_SIZE + 1; ++y)
                {
                    V9[x][y] = 0;
                }
            }

            for (var x = 0; x < SharedConst.ADT_CELLS_PER_GRID; ++x)
            {
                for (var y = 0; y < SharedConst.ADT_CELLS_PER_GRID; ++y)
                {
                    for (var z = 0; z < 8; ++z)
                    {
                        Holes[x][y][z] = 0;
                    }
                }
            }

            bool hasHoles     = false;
            bool hasFlightBox = false;

            foreach (var fileChunk in adt.chunks.LookupByKey("MCNK"))
            {
                MCNK mcnk = fileChunk.As <MCNK>();

                // Area data
                AreaIDs[mcnk.IndexY][mcnk.IndexX] = (ushort)mcnk.AreaID;

                // Height
                // Height values for triangles stored in order:
                // 1     2     3     4     5     6     7     8     9
                //    10    11    12    13    14    15    16    17
                // 18    19    20    21    22    23    24    25    26
                //    27    28    29    30    31    32    33    34
                // . . . . . . . .
                // For better get height values merge it to V9 and V8 map
                // V9 height map:
                // 1     2     3     4     5     6     7     8     9
                // 18    19    20    21    22    23    24    25    26
                // . . . . . . . .
                // V8 height map:
                //    10    11    12    13    14    15    16    17
                //    27    28    29    30    31    32    33    34
                // . . . . . . . .

                // Set map height as grid height
                for (int y = 0; y <= SharedConst.ADT_CELL_SIZE; y++)
                {
                    int cy = (int)mcnk.IndexY * SharedConst.ADT_CELL_SIZE + y;
                    for (int x = 0; x <= SharedConst.ADT_CELL_SIZE; x++)
                    {
                        int cx = (int)mcnk.IndexX * SharedConst.ADT_CELL_SIZE + x;
                        V9[cy][cx] = mcnk.ypos;
                    }
                }

                for (int y = 0; y < SharedConst.ADT_CELL_SIZE; y++)
                {
                    int cy = (int)mcnk.IndexY * SharedConst.ADT_CELL_SIZE + y;
                    for (int x = 0; x < SharedConst.ADT_CELL_SIZE; x++)
                    {
                        int cx = (int)mcnk.IndexX * SharedConst.ADT_CELL_SIZE + x;
                        V8[cy][cx] = mcnk.ypos;
                    }
                }

                // Get custom height
                FileChunk chunk = fileChunk.GetSubChunk("MCVT");
                if (chunk != null)
                {
                    MCVT mcvt = chunk.As <MCVT>();

                    // get V9 height map
                    for (int y = 0; y <= SharedConst.ADT_CELL_SIZE; y++)
                    {
                        int cy = (int)mcnk.IndexY * SharedConst.ADT_CELL_SIZE + y;
                        for (int x = 0; x <= SharedConst.ADT_CELL_SIZE; x++)
                        {
                            int cx = (int)mcnk.IndexX * SharedConst.ADT_CELL_SIZE + x;
                            V9[cy][cx] += mcvt.HeightMap[y * (SharedConst.ADT_CELL_SIZE * 2 + 1) + x];
                        }
                    }
                    // get V8 height map
                    for (int y = 0; y < SharedConst.ADT_CELL_SIZE; y++)
                    {
                        int cy = (int)mcnk.IndexY * SharedConst.ADT_CELL_SIZE + y;
                        for (int x = 0; x < SharedConst.ADT_CELL_SIZE; x++)
                        {
                            int cx = (int)mcnk.IndexX * SharedConst.ADT_CELL_SIZE + x;
                            V8[cy][cx] += mcvt.HeightMap[y * (SharedConst.ADT_CELL_SIZE * 2 + 1) + SharedConst.ADT_CELL_SIZE + 1 + x];
                        }
                    }
                }

                // Liquid data
                if (mcnk.MCLQCount > 8)
                {
                    FileChunk lquidChunk = fileChunk.GetSubChunk("MCLQ");
                    if (lquidChunk != null)
                    {
                        MCLQ liquid = lquidChunk.As <MCLQ>();
                        int  count  = 0;
                        for (int y = 0; y < SharedConst.ADT_CELL_SIZE; ++y)
                        {
                            int cy = (int)mcnk.IndexY * SharedConst.ADT_CELL_SIZE + y;
                            for (int x = 0; x < SharedConst.ADT_CELL_SIZE; ++x)
                            {
                                int cx = (int)mcnk.IndexX * SharedConst.ADT_CELL_SIZE + x;
                                if (liquid.Flags[y][x] != 0x0F)
                                {
                                    LiquidSnow[cy][cx] = true;
                                    if (!ignoreDeepWater && Convert.ToBoolean(liquid.Flags[y][x] & (1 << 7)))
                                    {
                                        LiquidFlags[mcnk.IndexY][mcnk.IndexX] |= LiquidHeaderTypeFlags.DarkWater;
                                    }
                                    ++count;
                                }
                            }
                        }

                        if (mcnk.Flags.HasAnyFlag(MCNKFlags.LiquidRiver))
                        {
                            LiquidEntries[mcnk.IndexY][mcnk.IndexX] = 1;
                            LiquidFlags[mcnk.IndexY][mcnk.IndexX]  |= LiquidHeaderTypeFlags.Water;           // water
                        }
                        if (mcnk.Flags.HasAnyFlag(MCNKFlags.LiquidOcean))
                        {
                            LiquidEntries[mcnk.IndexY][mcnk.IndexX] = 2;
                            LiquidFlags[mcnk.IndexY][mcnk.IndexX]  |= LiquidHeaderTypeFlags.Ocean;           // ocean
                        }
                        if (mcnk.Flags.HasAnyFlag(MCNKFlags.LiquidMagma))
                        {
                            LiquidEntries[mcnk.IndexY][mcnk.IndexX] = 3;
                            LiquidFlags[mcnk.IndexY][mcnk.IndexX]  |= LiquidHeaderTypeFlags.Magma;           // magma
                        }
                        if (mcnk.Flags.HasAnyFlag(MCNKFlags.LiquidSlime))
                        {
                            LiquidEntries[mcnk.IndexY][mcnk.IndexX] = 4;
                            LiquidFlags[mcnk.IndexY][mcnk.IndexX]  |= LiquidHeaderTypeFlags.Slime;           // slime
                        }

                        if (count == 0 && LiquidFlags[mcnk.IndexY][mcnk.IndexX] != 0)
                        {
                            Console.WriteLine("Wrong liquid detect in MCLQ chunk");
                        }

                        for (int y = 0; y <= SharedConst.ADT_CELL_SIZE; ++y)
                        {
                            int cy = (int)mcnk.IndexY * SharedConst.ADT_CELL_SIZE + y;
                            for (int x = 0; x <= SharedConst.ADT_CELL_SIZE; ++x)
                            {
                                int cx = (int)mcnk.IndexX * SharedConst.ADT_CELL_SIZE + x;
                                LiquidHeight[cy][cx] = liquid.Liquid[y][x].Height;
                            }
                        }
                    }
                }

                // Hole data
                if (!mcnk.Flags.HasAnyFlag(MCNKFlags.HighResHoles))
                {
                    uint hole = mcnk.HolesLowRes;
                    if (hole != 0)
                    {
                        if (TransformToHighRes((ushort)hole, Holes[mcnk.IndexY][mcnk.IndexX]))
                        {
                            hasHoles = true;
                        }
                    }
                }
                else
                {
                    Buffer.BlockCopy(mcnk.HighResHoles, 0, Holes[mcnk.IndexY][mcnk.IndexX], 0, 8);
                    if (BitConverter.ToUInt64(Holes[mcnk.IndexY][mcnk.IndexX], 0) != 0)
                    {
                        hasHoles = true;
                    }
                }
            }

            // Get liquid map for grid (in WOTLK used MH2O chunk)
            FileChunk chunkMH2O = adt.GetChunk("MH2O");

            if (chunkMH2O != null)
            {
                MH2O h2o = chunkMH2O.As <MH2O>();
                for (int i = 0; i < SharedConst.ADT_CELLS_PER_GRID; i++)
                {
                    for (int j = 0; j < SharedConst.ADT_CELLS_PER_GRID; j++)
                    {
                        MH2OInstance?h = h2o.GetLiquidInstance(i, j);
                        if (!h.HasValue)
                        {
                            continue;
                        }

                        MH2OInstance       adtLiquidHeader = h.Value;
                        MH2OChunkAttribute?attrs           = h2o.GetLiquidAttributes(i, j);

                        int   count      = 0;
                        ulong existsMask = h2o.GetLiquidExistsBitmap(adtLiquidHeader);
                        for (int y = 0; y < adtLiquidHeader.GetHeight(); y++)
                        {
                            int cy = i * SharedConst.ADT_CELL_SIZE + y + adtLiquidHeader.GetOffsetY();
                            for (int x = 0; x < adtLiquidHeader.GetWidth(); x++)
                            {
                                int cx = j * SharedConst.ADT_CELL_SIZE + x + adtLiquidHeader.GetOffsetX();
                                if (Convert.ToBoolean(existsMask & 1))
                                {
                                    LiquidSnow[cy][cx] = true;
                                    ++count;
                                }
                                existsMask >>= 1;
                            }
                        }

                        LiquidEntries[i][j] = h2o.GetLiquidType(adtLiquidHeader);
                        if (LiquidEntries[i][j] != 0)
                        {
                            var liquidTypeRecord = LiquidTypeStorage[LiquidEntries[i][j]];
                            switch ((LiquidType)liquidTypeRecord.SoundBank)
                            {
                            case LiquidType.Water:
                                LiquidFlags[i][j] |= LiquidHeaderTypeFlags.Water;
                                break;

                            case LiquidType.Ocean:
                                LiquidFlags[i][j] |= LiquidHeaderTypeFlags.Ocean;
                                if (!ignoreDeepWater && attrs.Value.Deep != 0)
                                {
                                    LiquidFlags[i][j] |= LiquidHeaderTypeFlags.DarkWater;
                                }
                                break;

                            case LiquidType.Magma:
                                LiquidFlags[i][j] |= LiquidHeaderTypeFlags.Magma;
                                break;

                            case LiquidType.Slime:
                                LiquidFlags[i][j] |= LiquidHeaderTypeFlags.Slime;
                                break;

                            default:
                                Console.WriteLine($"\nCan't find Liquid type {adtLiquidHeader.LiquidType} for map {mapName}\nchunk {i},{j}\n");
                                break;
                            }

                            if (count == 0 && LiquidFlags[i][j] != 0)
                            {
                                Console.WriteLine("Wrong liquid detect in MH2O chunk");
                            }
                        }
                        else
                        {
                            Console.WriteLine($"LiquidEntries is 0 for [{i}][{j}] ({i * j})");
                        }

                        int pos = 0;
                        for (int y = 0; y <= adtLiquidHeader.GetHeight(); y++)
                        {
                            int cy = i * SharedConst.ADT_CELL_SIZE + y + adtLiquidHeader.GetOffsetY();
                            for (int x = 0; x <= adtLiquidHeader.GetWidth(); x++)
                            {
                                int cx = j * SharedConst.ADT_CELL_SIZE + x + adtLiquidHeader.GetOffsetX();

                                LiquidHeight[cy][cx] = h2o.GetLiquidHeight(adtLiquidHeader, i, j, pos);

                                pos++;
                            }
                        }
                    }
                }
            }

            FileChunk chunkMFBO = adt.GetChunk("MFBO");

            if (chunkMFBO != null)
            {
                MFBO mfbo = chunkMFBO.As <MFBO>();
                for (var i = 0; i < 3; ++i)
                {
                    FlightBoxMax[i][0] = mfbo.Max.coords[0 + i * 3];
                    FlightBoxMax[i][1] = mfbo.Max.coords[1 + i * 3];
                    FlightBoxMax[i][2] = mfbo.Max.coords[2 + i * 3];

                    FlightBoxMin[i][0] = mfbo.Min.coords[0 + i * 3];
                    FlightBoxMin[i][1] = mfbo.Min.coords[1 + i * 3];
                    FlightBoxMin[i][2] = mfbo.Min.coords[2 + i * 3];
                }
                hasFlightBox = true;
            }

            //============================================
            // Try pack area data
            //============================================
            bool fullAreaData = false;
            uint areaId       = AreaIDs[0][0];

            for (int y = 0; y < SharedConst.ADT_CELLS_PER_GRID; ++y)
            {
                for (int x = 0; x < SharedConst.ADT_CELLS_PER_GRID; ++x)
                {
                    if (AreaIDs[y][x] != areaId)
                    {
                        fullAreaData = true;
                        break;
                    }
                }
            }

            map.areaMapOffset = 44;
            map.areaMapSize   = 8;

            MapAreaHeader areaHeader;

            areaHeader.fourcc = SharedConst.MAP_AREA_MAGIC;
            areaHeader.flags  = 0;
            if (fullAreaData)
            {
                areaHeader.gridArea = 0;
                map.areaMapSize    += 512;
            }
            else
            {
                areaHeader.flags   |= AreaHeaderFlags.NoArea;
                areaHeader.gridArea = (ushort)areaId;
            }

            //============================================
            // Try pack height data
            //============================================
            float maxHeight = -20000;
            float minHeight = 20000;

            for (int y = 0; y < SharedConst.ADT_GRID_SIZE; y++)
            {
                for (int x = 0; x < SharedConst.ADT_GRID_SIZE; x++)
                {
                    float h = V8[y][x];
                    if (maxHeight < h)
                    {
                        maxHeight = h;
                    }
                    if (minHeight > h)
                    {
                        minHeight = h;
                    }
                }
            }
            for (int y = 0; y <= SharedConst.ADT_GRID_SIZE; y++)
            {
                for (int x = 0; x <= SharedConst.ADT_GRID_SIZE; x++)
                {
                    float h = V9[y][x];
                    if (maxHeight < h)
                    {
                        maxHeight = h;
                    }
                    if (minHeight > h)
                    {
                        minHeight = h;
                    }
                }
            }

            // Check for allow limit minimum height (not store height in deep ochean - allow save some memory)
            if (minHeight < -2000.0f)
            {
                for (int y = 0; y < SharedConst.ADT_GRID_SIZE; y++)
                {
                    for (int x = 0; x < SharedConst.ADT_GRID_SIZE; x++)
                    {
                        if (V8[y][x] < -2000.0f)
                        {
                            V8[y][x] = -2000.0f;
                        }
                    }
                }
                for (int y = 0; y <= SharedConst.ADT_GRID_SIZE; y++)
                {
                    for (int x = 0; x <= SharedConst.ADT_GRID_SIZE; x++)
                    {
                        if (V9[y][x] < -2000.0f)
                        {
                            V9[y][x] = -2000.0f;
                        }
                    }
                }

                if (minHeight < -2000.0f)
                {
                    minHeight = -2000.0f;
                }
                if (maxHeight < -2000.0f)
                {
                    maxHeight = -2000.0f;
                }
            }

            map.heightMapOffset = map.areaMapOffset + map.areaMapSize;
            map.heightMapSize   = (uint)Marshal.SizeOf <MapHeightHeader>();

            MapHeightHeader heightHeader;

            heightHeader.fourcc        = SharedConst.MAP_HEIGHT_MAGIC;
            heightHeader.flags         = 0;
            heightHeader.gridHeight    = minHeight;
            heightHeader.gridMaxHeight = maxHeight;

            if (maxHeight == minHeight)
            {
                heightHeader.flags |= HeightHeaderFlags.NoHeight;
            }

            // Not need store if flat surface
            if ((maxHeight - minHeight) < 0.005f)
            {
                heightHeader.flags |= HeightHeaderFlags.NoHeight;
            }

            if (hasFlightBox)
            {
                heightHeader.flags |= HeightHeaderFlags.HasFlightBounds;
                map.heightMapSize  += 18 + 18;
            }

            // Try store as packed in uint16 or uint8 values
            if (!heightHeader.flags.HasFlag(HeightHeaderFlags.NoHeight))
            {
                float step = 0;
                // Try Store as uint values
                if (true)//CONF_allow_float_to_int
                {
                    float diff = maxHeight - minHeight;
                    if (diff < 2.0f)      // As uint8 (max accuracy = CONF_float_to_int8_limit/256)
                    {
                        heightHeader.flags |= HeightHeaderFlags.AsInt8;
                        step = 255 / diff;
                    }
                    else if (diff < 2048.0f)  // As uint16 (max accuracy = CONF_float_to_int16_limit/65536)
                    {
                        heightHeader.flags |= HeightHeaderFlags.AsInt16;
                        step = 65535 / diff;
                    }
                }

                // Pack it to int values if need
                if (heightHeader.flags.HasFlag(HeightHeaderFlags.AsInt8))
                {
                    for (int y = 0; y < SharedConst.ADT_GRID_SIZE; y++)
                    {
                        for (int x = 0; x < SharedConst.ADT_GRID_SIZE; x++)
                        {
                            UInt8_V8[y][x] = (byte)((V8[y][x] - minHeight) * step + 0.5f);
                        }
                    }
                    for (int y = 0; y <= SharedConst.ADT_GRID_SIZE; y++)
                    {
                        for (int x = 0; x <= SharedConst.ADT_GRID_SIZE; x++)
                        {
                            UInt8_V9[y][x] = (byte)((V9[y][x] - minHeight) * step + 0.5f);
                        }
                    }
                    map.heightMapSize += 16641 + 16384;
                }
                else if (heightHeader.flags.HasFlag(HeightHeaderFlags.AsInt16))
                {
                    for (int y = 0; y < SharedConst.ADT_GRID_SIZE; y++)
                    {
                        for (int x = 0; x < SharedConst.ADT_GRID_SIZE; x++)
                        {
                            UInt16_V8[y][x] = (ushort)((V8[y][x] - minHeight) * step + 0.5f);
                        }
                    }

                    for (int y = 0; y <= SharedConst.ADT_GRID_SIZE; y++)
                    {
                        for (int x = 0; x <= SharedConst.ADT_GRID_SIZE; x++)
                        {
                            UInt16_V9[y][x] = (ushort)((V9[y][x] - minHeight) * step + 0.5f);
                        }
                    }

                    map.heightMapSize += 33282 + 32768;
                }
                else
                {
                    map.heightMapSize += 66564 + 65536;
                }
            }

            //============================================
            // Pack liquid data
            //============================================
            ushort firstLiquidType = LiquidEntries[0][0];
            LiquidHeaderTypeFlags firstLiquidFlag = LiquidFlags[0][0];
            bool fullType = false;

            for (int y = 0; y < SharedConst.ADT_CELLS_PER_GRID; y++)
            {
                for (int x = 0; x < SharedConst.ADT_CELLS_PER_GRID; x++)
                {
                    if (LiquidEntries[y][x] != firstLiquidType || LiquidFlags[y][x] != firstLiquidFlag)
                    {
                        fullType = true;
                        y        = SharedConst.ADT_CELLS_PER_GRID;
                        break;
                    }
                }
            }

            MapLiquidHeader mapLiquidHeader = new();

            // no water data (if all grid have 0 liquid type)
            if (firstLiquidFlag == 0 && !fullType)
            {
                // No liquid data
                map.liquidMapOffset = 0;
                map.liquidMapSize   = 0;
            }
            else
            {
                int minX = 255, minY = 255;
                int maxX = 0, maxY = 0;
                maxHeight = -20000;
                minHeight = 20000;
                for (int y = 0; y < SharedConst.ADT_GRID_SIZE; y++)
                {
                    for (int x = 0; x < SharedConst.ADT_GRID_SIZE; x++)
                    {
                        if (LiquidSnow[y][x])
                        {
                            if (minX > x)
                            {
                                minX = x;
                            }
                            if (maxX < x)
                            {
                                maxX = x;
                            }
                            if (minY > y)
                            {
                                minY = y;
                            }
                            if (maxY < y)
                            {
                                maxY = y;
                            }
                            float h = LiquidHeight[y][x];
                            if (maxHeight < h)
                            {
                                maxHeight = h;
                            }
                            if (minHeight > h)
                            {
                                minHeight = h;
                            }
                        }
                        else
                        {
                            LiquidHeight[y][x] = -2000.0f;
                        }
                    }
                }
                map.liquidMapOffset = map.heightMapOffset + map.heightMapSize;
                map.liquidMapSize   = (uint)Marshal.SizeOf <MapLiquidHeader>();

                mapLiquidHeader.fourcc      = SharedConst.MAP_LIQUID_MAGIC;
                mapLiquidHeader.flags       = LiquidHeaderFlags.None;
                mapLiquidHeader.liquidType  = 0;
                mapLiquidHeader.offsetX     = (byte)minX;
                mapLiquidHeader.offsetY     = (byte)minY;
                mapLiquidHeader.width       = (byte)(maxX - minX + 1 + 1);
                mapLiquidHeader.height      = (byte)(maxY - minY + 1 + 1);
                mapLiquidHeader.liquidLevel = minHeight;

                if (maxHeight == minHeight)
                {
                    mapLiquidHeader.flags |= LiquidHeaderFlags.NoHeight;
                }

                // Not need store if flat surface
                if ((maxHeight - minHeight) < 0.001f)
                {
                    mapLiquidHeader.flags |= LiquidHeaderFlags.NoHeight;
                }

                if (!fullType)
                {
                    mapLiquidHeader.flags |= LiquidHeaderFlags.NoType;
                }

                if (mapLiquidHeader.flags.HasFlag(LiquidHeaderFlags.NoType))
                {
                    mapLiquidHeader.liquidFlags = firstLiquidFlag;
                    mapLiquidHeader.liquidType  = firstLiquidType;
                }
                else
                {
                    map.liquidMapSize += 512 + 256;
                }

                if (!mapLiquidHeader.flags.HasFlag(LiquidHeaderFlags.NoHeight))
                {
                    map.liquidMapSize += (uint)(sizeof(float) * mapLiquidHeader.width * mapLiquidHeader.height);
                }
            }

            if (hasHoles)
            {
                if (map.liquidMapOffset != 0)
                {
                    map.holesOffset = map.liquidMapOffset + map.liquidMapSize;
                }
                else
                {
                    map.holesOffset = map.heightMapOffset + map.heightMapSize;
                }

                map.holesSize = 2048;
            }
            else
            {
                map.holesOffset = 0;
                map.holesSize   = 0;
            }

            // Ok all data prepared - store it
            using (BinaryWriter binaryWriter = new(File.Open(outputPath, FileMode.Create, FileAccess.Write)))
            {
                binaryWriter.WriteStruct(map);
                // Store area data
                binaryWriter.WriteStruct(areaHeader);
                if (!areaHeader.flags.HasFlag(AreaHeaderFlags.NoArea))
                {
                    for (var x = 0; x < AreaIDs.Length; ++x)
                    {
                        for (var y = 0; y < AreaIDs[x].Length; ++y)
                        {
                            binaryWriter.Write(AreaIDs[x][y]);
                        }
                    }
                }

                // Store height data
                binaryWriter.WriteStruct(heightHeader);
                if (!heightHeader.flags.HasFlag(HeightHeaderFlags.NoHeight))
                {
                    if (heightHeader.flags.HasFlag(HeightHeaderFlags.AsInt16))
                    {
                        for (var x = 0; x < UInt16_V9.Length; ++x)
                        {
                            for (var y = 0; y < UInt16_V9[x].Length; ++y)
                            {
                                binaryWriter.Write(UInt16_V9[x][y]);
                            }
                        }

                        for (var x = 0; x < UInt16_V8.Length; ++x)
                        {
                            for (var y = 0; y < UInt16_V8[x].Length; ++y)
                            {
                                binaryWriter.Write(UInt16_V8[x][y]);
                            }
                        }
                    }
                    else if (heightHeader.flags.HasFlag(HeightHeaderFlags.AsInt8))
                    {
                        for (var x = 0; x < UInt8_V9.Length; ++x)
                        {
                            for (var y = 0; y < UInt8_V9[x].Length; ++y)
                            {
                                binaryWriter.Write(UInt8_V9[x][y]);
                            }
                        }

                        for (var x = 0; x < UInt8_V8.Length; ++x)
                        {
                            for (var y = 0; y < UInt8_V8[x].Length; ++y)
                            {
                                binaryWriter.Write(UInt8_V8[x][y]);
                            }
                        }
                    }
                    else
                    {
                        for (var x = 0; x < V9.Length; ++x)
                        {
                            for (var y = 0; y < V9[x].Length; ++y)
                            {
                                binaryWriter.Write(V9[x][y]);
                            }
                        }

                        for (var x = 0; x < V8.Length; ++x)
                        {
                            for (var y = 0; y < V8[x].Length; ++y)
                            {
                                binaryWriter.Write(V8[x][y]);
                            }
                        }
                    }
                }

                if (heightHeader.flags.HasFlag(HeightHeaderFlags.HasFlightBounds))
                {
                    for (var x = 0; x < 3; ++x)
                    {
                        for (var y = 0; y < 3; ++y)
                        {
                            binaryWriter.Write(FlightBoxMax[x][y]);
                        }
                    }

                    for (var x = 0; x < 3; ++x)
                    {
                        for (var y = 0; y < 3; ++y)
                        {
                            binaryWriter.Write(FlightBoxMin[x][y]);
                        }
                    }
                }

                // Store liquid data if need
                if (map.liquidMapOffset != 0)
                {
                    binaryWriter.WriteStruct(mapLiquidHeader);
                    if (!mapLiquidHeader.flags.HasFlag(LiquidHeaderFlags.NoType))
                    {
                        for (var x = 0; x < LiquidEntries.Length; ++x)
                        {
                            for (var y = 0; y < LiquidEntries[x].Length; ++y)
                            {
                                binaryWriter.Write(LiquidEntries[x][y]);
                            }
                        }

                        for (var x = 0; x < LiquidFlags.Length; ++x)
                        {
                            for (var y = 0; y < LiquidFlags[x].Length; ++y)
                            {
                                binaryWriter.Write((byte)LiquidFlags[x][y]);
                            }
                        }
                    }

                    if (!mapLiquidHeader.flags.HasFlag(LiquidHeaderFlags.NoHeight))
                    {
                        for (int y = 0; y < mapLiquidHeader.height; y++)
                        {
                            for (int x = 0; x < mapLiquidHeader.width; x++)
                            {
                                binaryWriter.Write(LiquidHeight[y + mapLiquidHeader.offsetY][x + mapLiquidHeader.offsetX]);
                            }
                        }
                    }
                }

                // store hole data
                if (hasHoles)
                {
                    for (var x = 0; x < Holes.Length; ++x)
                    {
                        for (var y = 0; y < Holes[x].Length; ++y)
                        {
                            for (var z = 0; z < Holes[x][y].Length; ++z)
                            {
                                binaryWriter.Write(Holes[x][y][z]);
                            }
                        }
                    }
                }
            }

            return(true);
        }