BinaryWriterExtensions.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Runtime.CompilerServices;
  6. using System.Text;
  7. using MineCase.Nbt;
  8. using MineCase.Nbt.Serialization;
  9. using MineCase.Nbt.Tags;
  10. using MineCase.Protocol;
  11. using MineCase.Protocol.Play;
  12. namespace MineCase.Serialization
  13. {
  14. internal static class BinaryWriterExtensions
  15. {
  16. public static void WriteAsBoolean(this BinaryWriter bw, bool value) =>
  17. bw.Write(value);
  18. public static void WriteAsByte(this BinaryWriter bw, byte value) =>
  19. bw.Write(value);
  20. // http://wiki.vg/Protocol#VarInt_and_VarLong
  21. public static void WriteAsVarInt(this BinaryWriter bw, uint value, out uint bytesWrite)
  22. {
  23. uint numWrite = 0;
  24. do
  25. {
  26. byte temp = (byte)(value & 0b01111111);
  27. value >>= 7;
  28. if (value != 0)
  29. temp |= 0b10000000;
  30. bw.Write(temp);
  31. numWrite++;
  32. }
  33. while (value != 0);
  34. bytesWrite = numWrite;
  35. }
  36. public static void WriteAsByteArray(this BinaryWriter bw, byte[] value) =>
  37. bw.Write(value);
  38. public static void WriteAsIntArray(this BinaryWriter bw, int[] value)
  39. {
  40. foreach (var eachInt in value)
  41. bw.WriteAsInt(eachInt);
  42. }
  43. public static void WriteAsVarIntArray(this BinaryWriter bw, uint[] value)
  44. {
  45. foreach (var eachInt in value)
  46. bw.WriteAsVarInt(eachInt, out _);
  47. }
  48. public static void WriteAsSlotArray(this BinaryWriter bw, Slot[] value)
  49. {
  50. foreach (var slot in value)
  51. bw.WriteAsSlot(slot);
  52. }
  53. public static void WriteAsString(this BinaryWriter bw, string value)
  54. {
  55. var bytes = Encoding.UTF8.GetBytes(value);
  56. bw.WriteAsVarInt((uint)bytes.Length, out _);
  57. bw.Write(bytes);
  58. }
  59. public static void WriteAsChat(this BinaryWriter bw, Chat value)
  60. {
  61. bw.WriteAsString(value.ToString());
  62. }
  63. public static void WriteAsShort(this BinaryWriter bw, short value)
  64. {
  65. bw.Write(((ushort)value).ToBigEndian());
  66. }
  67. public static void WriteAsInt(this BinaryWriter bw, int value)
  68. {
  69. bw.Write(((uint)value).ToBigEndian());
  70. }
  71. public static void WriteAsLong(this BinaryWriter bw, long value)
  72. {
  73. bw.Write(((ulong)value).ToBigEndian());
  74. }
  75. public static void WriteAsUnsignedShort(this BinaryWriter bw, ushort value)
  76. {
  77. bw.Write(value.ToBigEndian());
  78. }
  79. public static void WriteAsUnsignedLong(this BinaryWriter bw, ulong value)
  80. {
  81. bw.Write(value.ToBigEndian());
  82. }
  83. public static void WriteAsFloat(this BinaryWriter bw, float value)
  84. {
  85. var uintValue = Unsafe.As<float, uint>(ref value);
  86. bw.Write(uintValue.ToBigEndian());
  87. }
  88. public static void WriteAsDouble(this BinaryWriter bw, double value)
  89. {
  90. var ulongValue = Unsafe.As<double, ulong>(ref value);
  91. bw.Write(ulongValue.ToBigEndian());
  92. }
  93. public static void WriteAsUUID(this BinaryWriter bw, Guid value)
  94. {
  95. bw.Write(value.ToByteArray());
  96. }
  97. public static void WriteAsAngle(this BinaryWriter bw, Angle value)
  98. {
  99. bw.Write(value.Value);
  100. }
  101. public static void WriteAsArray<T>(this BinaryWriter bw, IReadOnlyList<T> array)
  102. where T : IPacket
  103. {
  104. foreach (var item in array)
  105. item.Serialize(bw);
  106. }
  107. public static void WriteAsNbtTag(this BinaryWriter bw, NbtCompound value)
  108. {
  109. NbtTagSerializer.SerializeTag(value, bw);
  110. }
  111. public static void WriteAsNbtTagArray(this BinaryWriter bw, NbtCompound[] data)
  112. {
  113. for (int i = 0; i < data.Length; ++i)
  114. {
  115. bw.WriteAsNbtTag(data[i]);
  116. }
  117. }
  118. public static void WriteAsPosition(this BinaryWriter bw, Position position)
  119. {
  120. Debug.Assert(IsValueInRangeInclusive(position.X, -33554432, 33554431), "position X value not in range.");
  121. Debug.Assert(IsValueInRangeInclusive(position.Y, -2048, 2047), "position Y value not in range.");
  122. Debug.Assert(IsValueInRangeInclusive(position.Z, -33554432, 33554431), "position Z value not in range.");
  123. var value = (ulong)((uint)position.X & 0x3FFFFFF) << 38;
  124. value |= (ulong)((uint)position.Y & 0xFFF);
  125. value |= (ulong)((uint)position.Z & 0x3FFFFFF) << 12;
  126. bw.WriteAsUnsignedLong(value);
  127. }
  128. private static bool IsValueInRangeInclusive(long value, long min, long max)
  129. {
  130. return (value >= min) && (value <= max);
  131. }
  132. public static void WriteAsSlot(this BinaryWriter bw, Slot slot)
  133. {
  134. bw.WriteAsBoolean(!slot.IsEmpty);
  135. if (!slot.IsEmpty)
  136. {
  137. bw.WriteAsVarInt((uint)slot.BlockId, out _);
  138. bw.WriteAsByte(slot.ItemCount);
  139. if (slot.NBT != null)
  140. slot.NBT.WriteTo(bw.BaseStream);
  141. else
  142. bw.WriteAsByte(0);
  143. }
  144. }
  145. public static BinaryWriter WriteAsEntityMetadata(this BinaryWriter bw, byte index, EntityMetadataType type)
  146. {
  147. bw.WriteAsByte(index);
  148. bw.WriteAsByte((byte)type);
  149. return bw;
  150. }
  151. }
  152. internal static class DataTypeSizeExtensions
  153. {
  154. public static uint SizeOfVarInt(this uint value)
  155. {
  156. uint numWrite = 0;
  157. do
  158. {
  159. value >>= 7;
  160. numWrite++;
  161. }
  162. while (value != 0);
  163. return numWrite;
  164. }
  165. public static ushort ToBigEndian(this ushort value)
  166. {
  167. return (ushort)((value >> 8) | (((byte)value) << 8));
  168. }
  169. public static uint ToBigEndian(this uint value)
  170. {
  171. return (value >> 24) | ((value & 0x00FF_0000) >> 8) |
  172. ((value & 0x0000_FF00) << 8) | ((value & 0x0000_00FF) << 24);
  173. }
  174. public static ulong ToBigEndian(this ulong value)
  175. {
  176. return (value >> 56) | ((value & 0x00FF_0000_0000_0000) >> 40) | ((value & 0x0000_FF00_0000_0000) >> 24) |
  177. ((value & 0x0000_00FF_0000_0000) >> 8) | ((value & 0x0000_0000_FF00_0000) << 8) | ((value & 0x0000_0000_00FF_0000) << 24) |
  178. ((value & 0x0000_0000_0000_FF00) << 40) | ((value & 0x0000_0000_0000_00FF) << 56);
  179. }
  180. }
  181. }