using System; using System.Buffers; using System.IO; using Server.Compression; namespace Server.Network { public class BeginHouseCustomization : Packet { public BeginHouseCustomization(Serial house) : base(0xBF) { EnsureCapacity(17); Stream.Write((short)0x20); Stream.Write(house); Stream.Write((byte)0x04); Stream.Write((ushort)0x0000); Stream.Write((ushort)0xFFFF); Stream.Write((ushort)0xFFFF); Stream.Write((byte)0xFF); } } public class EndHouseCustomization : Packet { public EndHouseCustomization(Serial house) : base(0xBF) { EnsureCapacity(17); Stream.Write((short)0x20); Stream.Write(house); Stream.Write((byte)0x05); Stream.Write((ushort)0x0000); Stream.Write((ushort)0xFFFF); Stream.Write((ushort)0xFFFF); Stream.Write((byte)0xFF); } } public sealed class DesignStateGeneral : Packet { public DesignStateGeneral(Serial house, int revision) : base(0xBF) { EnsureCapacity(13); Stream.Write((short)0x1D); Stream.Write(house); Stream.Write(revision); } } public sealed class DesignStateDetailed : Packet { public const int MaxItemsPerStairBuffer = 750; private readonly bool[] m_PlaneUsed = new bool[9]; private readonly byte[] m_PrimBuffer = new byte[4]; public DesignStateDetailed(Serial serial, int revision, int xMin, int yMin, int xMax, int yMax, MultiTileEntry[] tiles) : base(0xD8) { EnsureCapacity(17 + tiles.Length * 5); Write((byte)0x03); // Compression Type Write((byte)0x00); // Unknown Write(serial.Value); Write(revision); Write((short)tiles.Length); Write((short)0); // Buffer length : reserved Write((byte)0); // Plane count : reserved var totalLength = 1; // includes plane count var width = xMax - xMin + 1; var height = yMax - yMin + 1; var planeBuffers = new byte[9][]; for (var i = 0; i < planeBuffers.Length; ++i) { planeBuffers[i] = ArrayPool.Shared.Rent(0x400); } var stairBuffers = new byte[6][]; for (var i = 0; i < stairBuffers.Length; ++i) { stairBuffers[i] = ArrayPool.Shared.Rent(MaxItemsPerStairBuffer * 5); } Clear(planeBuffers[0], width * height * 2); for (var i = 0; i < 4; ++i) { Clear(planeBuffers[1 + i], (width - 1) * (height - 2) * 2); Clear(planeBuffers[5 + i], width * (height - 1) * 2); } var totalStairsUsed = 0; for (var i = 0; i < tiles.Length; ++i) { var mte = tiles[i]; var x = mte.OffsetX - xMin; var y = mte.OffsetY - yMin; int z = mte.OffsetZ; var floor = TileData.ItemTable[mte.ItemId & TileData.MaxItemValue].Height <= 0; int plane, size; switch (z) { case 0: plane = 0; break; case 7: plane = 1; break; case 27: plane = 2; break; case 47: plane = 3; break; case 67: plane = 4; break; default: { var stairBufferIndex = totalStairsUsed / MaxItemsPerStairBuffer; var stairBuffer = stairBuffers[stairBufferIndex]; var byteIndex = totalStairsUsed % MaxItemsPerStairBuffer * 5; stairBuffer[byteIndex++] = (byte)(mte.ItemId >> 8); stairBuffer[byteIndex++] = (byte)mte.ItemId; stairBuffer[byteIndex++] = (byte)mte.OffsetX; stairBuffer[byteIndex++] = (byte)mte.OffsetY; stairBuffer[byteIndex] = (byte)mte.OffsetZ; ++totalStairsUsed; continue; } } if (plane == 0) { size = height; } else if (floor) { size = height - 2; x -= 1; y -= 1; } else { size = height - 1; plane += 4; } var index = (x * size + y) * 2; if (x < 0 || y < 0 || y >= size || index + 1 >= 0x400) { var stairBufferIndex = totalStairsUsed / MaxItemsPerStairBuffer; var stairBuffer = stairBuffers[stairBufferIndex]; var byteIndex = totalStairsUsed % MaxItemsPerStairBuffer * 5; stairBuffer[byteIndex++] = (byte)(mte.ItemId >> 8); stairBuffer[byteIndex++] = (byte)mte.ItemId; stairBuffer[byteIndex++] = (byte)mte.OffsetX; stairBuffer[byteIndex++] = (byte)mte.OffsetY; stairBuffer[byteIndex] = (byte)mte.OffsetZ; ++totalStairsUsed; } else { m_PlaneUsed[plane] = true; planeBuffers[plane][index] = (byte)(mte.ItemId >> 8); planeBuffers[plane][index + 1] = (byte)mte.ItemId; } } var planeCount = 0; var deflatedBuffer = ArrayPool.Shared.Rent(0x2000); for (var i = 0; i < planeBuffers.Length; ++i) { if (!m_PlaneUsed[i]) { ArrayPool.Shared.Return(planeBuffers[i]); continue; } ++planeCount; int size = i switch { 0 => width * height * 2, < 5 => (width - 1) * (height - 2) * 2, _ => width * (height - 1) * 2 }; var inflatedBuffer = planeBuffers[i]; var deflatedLength = Deflate.Standard.Pack( deflatedBuffer, inflatedBuffer.AsSpan(0, size) ); if (deflatedLength == 0) { Console.WriteLine("Compression error"); } Write((byte)(0x20 | i)); Write((byte)size); Write((byte)deflatedLength); Write((byte)(((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF))); Write(deflatedBuffer, 0, deflatedLength); totalLength += 4 + deflatedLength; ArrayPool.Shared.Return(inflatedBuffer); } var totalStairBuffersUsed = (totalStairsUsed + (MaxItemsPerStairBuffer - 1)) / MaxItemsPerStairBuffer; for (var i = 0; i < totalStairBuffersUsed; ++i) { ++planeCount; var count = Math.Min(MaxItemsPerStairBuffer, totalStairsUsed - i * MaxItemsPerStairBuffer); var size = count * 5; var inflatedBuffer = stairBuffers[i]; var deflatedLength = Deflate.Standard.Pack( deflatedBuffer, inflatedBuffer.AsSpan(0, size) ); if (deflatedLength == 0) { Console.WriteLine("Compression error"); } Write((byte)(9 + i)); Write((byte)size); Write((byte)deflatedLength); Write((byte)(((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF))); Write(deflatedBuffer, 0, deflatedLength); totalLength += 4 + deflatedLength; } for (var i = 0; i < stairBuffers.Length; ++i) { ArrayPool.Shared.Return(stairBuffers[i]); } ArrayPool.Shared.Return(deflatedBuffer); Stream.Seek(15, SeekOrigin.Begin); Write((short)totalLength); // Buffer length Write((byte)planeCount); // Plane count } public void Write(int value) { m_PrimBuffer[0] = (byte)(value >> 24); m_PrimBuffer[1] = (byte)(value >> 16); m_PrimBuffer[2] = (byte)(value >> 8); m_PrimBuffer[3] = (byte)value; Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 4); } public void Write(uint value) { m_PrimBuffer[0] = (byte)(value >> 24); m_PrimBuffer[1] = (byte)(value >> 16); m_PrimBuffer[2] = (byte)(value >> 8); m_PrimBuffer[3] = (byte)value; Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 4); } public void Write(short value) { m_PrimBuffer[0] = (byte)(value >> 8); m_PrimBuffer[1] = (byte)value; Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 2); } public void Write(byte value) { Stream.UnderlyingStream.WriteByte(value); } public void Write(byte[] buffer, int offset, int size) { Stream.UnderlyingStream.Write(buffer, offset, size); } public static void Clear(byte[] buffer, int size) { for (var i = 0; i < size; ++i) { buffer[i] = 0; } } } }