Action <TeraBinaryReader, object> CompileDeserializer(Type type) { if (_deserializers.TryGetValue(type, out var d)) { return(d); } var reader = Expression.Parameter(typeof(TeraBinaryReader), "reader"); var target = Expression.Parameter(typeof(object), "target"); var packet = Expression.Variable(type, "packet"); BlockExpression CompileByteArray(PacketFieldInfo info) { var prop = info.Property; var offset = Expression.Variable(typeof(int), "offset"); var count = Expression.Variable(typeof(ushort), "count"); var property = Expression.Variable(prop.PropertyType, "property"); var position = Expression.Variable(typeof(int), "position"); var read = Expression.Block(new[] { position }, position.Assign(reader.Property(ReaderPositionName)), reader.Property(ReaderPositionName).Assign(offset), property.Call(AddRangeName, null, new[] { reader.Call(ReadBytesName, null, new[] { count.Convert(typeof(int)) }) }), reader.Property(ReaderPositionName).Assign(position)); return(Expression.Block(new[] { offset, count, property }, offset.Assign(reader.Call(ReadOffsetName, null, null)), count.Assign(reader.Call(ReadUInt16Name, null, null)), property.Assign(packet.Property(prop)), property.Call(ClearName, null, null), count.NotEqual(((ushort)0).Constant()).IfThen(read))); } BlockExpression CompileArray(PacketFieldInfo info) { var prop = info.Property; var ftype = prop.PropertyType; var elemType = ftype.GetGenericArguments()[0]; var offset = Expression.Variable(typeof(int), "offset"); var count = Expression.Variable(typeof(ushort), "count"); var position = Expression.Variable(typeof(int), "position"); var property = Expression.Variable(ftype, "property"); var next = Expression.Variable(typeof(int), "next"); var i = Expression.Variable(typeof(int), "i"); var elem = Expression.Variable(elemType, "elem"); var loop = CustomExpression.For(i, 0.Constant(), i.LessThan(count.Convert(typeof(int))), i.PostIncrementAssign(), Expression.Block( reader.Property(ReaderPositionName).Assign(next), reader.Call(ReadOffsetName, null, null), next.Assign(reader.Call(ReadOffsetName, null, null)), elem.Assign(elemType.New()), Expression.Invoke(CompileDeserializer(elemType) .Constant(), reader, elem), property.Call(AddName, null, new[] { elem }))); var read = Expression.Block(new[] { position, next, elem }, position.Assign(reader.Property(ReaderPositionName)), next.Assign(offset), loop, reader.Property(ReaderPositionName).Assign(position)); return(Expression.Block(new[] { offset, count, property }, count.Assign(reader.Call(ReadUInt16Name, null, null)), offset.Assign(reader.Call(ReadOffsetName, null, null)), property.Assign(packet.Property(prop)), property.Call(ClearName, null, null), count.NotEqual(((ushort)0).Constant()).IfThen(read))); } BlockExpression CompileString(PacketFieldInfo info) { var offset = Expression.Variable(typeof(int), "offset"); var position = Expression.Variable(typeof(int), "position"); return(Expression.Block(new[] { offset, position }, offset.Assign(reader.Call(ReadOffsetName, null, null)), position.Assign(reader.Property(ReaderPositionName)), reader.Property(ReaderPositionName).Assign(offset), packet.Property(info.Property) .Assign(reader.Call(ReadStringName, null, null)), reader.Property(ReaderPositionName).Assign(position))); } BlockExpression CompilePrimitive(PacketFieldInfo info) { var prop = info.Property; var ftype = prop.PropertyType; var etype = ftype.IsEnum ? ftype.GetEnumUnderlyingType() : ftype; var prefix = info.Attribute.IsLocalSkill ? LocalName : string.Empty; Expression read = reader.Call(ReadName + prefix + etype.Name, null, null); if (ftype.IsEnum) { read = read.Convert(ftype); } return(Expression.Block(packet.Property(prop).Assign(read))); } var exprs = new List <Expression>() { packet.Assign(target.Convert(type)) }; foreach (var info in GetPacketFields <PacketFieldInfo>(type)) { BlockExpression expr; if (info.IsByteArray) { expr = CompileByteArray(info); } else if (info.IsArray) { expr = CompileArray(info); } else if (info.IsString) { expr = CompileString(info); } else { expr = CompilePrimitive(info); } exprs.Add(expr); } return(Expression.Lambda <Action <TeraBinaryReader, object> >( Expression.Block(new[] { packet }, exprs), reader, target).Compile()); }
Action <GameBinaryReader, object> CompileDeserializer(PacketInfo info) { if (_deserializers.TryGetValue(info, out var d)) { return(d); } var reader = typeof(GameBinaryReader).Parameter("reader"); var target = typeof(object).Parameter("target"); var packet = info.Type.Variable("packet"); BlockExpression CompileByteArray(CompilerPacketFieldInfo info) { var prop = info.Property; var offset = typeof(int).Variable("offset"); var count = typeof(ushort).Variable("count"); var property = prop.PropertyType.Variable("property"); var position = typeof(int).Variable("position"); var read = Expression.Block(new[] { position }, position.Assign(reader.Property(PositionName)), reader.Property(PositionName).Assign(offset), property.Call(AddRangeName, null, new[] { reader.Call(ReadBytesName, null, new[] { count.Convert(typeof(int)) }) }), reader.Property(PositionName).Assign(position)); return(Expression.Block(new[] { offset, count, property }, offset.Assign(reader.Call(ReadOffsetName, null, null)), count.Assign(reader.Call(ReadUInt16Name, null, null)), property.Assign(packet.Property(prop)), property.Call(ClearName, null, null), count.NotEqual(((ushort)0).Constant()) .IfThen(read))); } BlockExpression CompileArray(CompilerPacketFieldInfo info) { var prop = info.Property; var ftype = prop.PropertyType; var elemInfo = GetPacketInfo(ftype.GetGenericArguments()[0]); var offset = typeof(int).Variable("offset"); var count = typeof(ushort).Variable("count"); var position = typeof(int).Variable("position"); var property = ftype.Variable("property"); var next = typeof(int).Variable("next"); var i = typeof(int).Variable("i"); var elem = elemInfo.Type.Variable("elem"); var loop = CustomExpression.For( i, 0.Constant(), i.LessThan(count.Convert(typeof(int))), i.PreIncrementAssign(), Expression.Block( reader.Property(PositionName).Assign(next), reader.Call(ReadOffsetName, null, null), next.Assign(reader.Call(ReadOffsetName, null, null)), elem.Assign(elemInfo.Type.New()), CompileDeserializer(elemInfo).Constant().Invoke(reader, elem), property.Call(AddName, null, new[] { elem }))); var read = Expression.Block(new[] { position, next, elem }, position.Assign(reader.Property(PositionName)), next.Assign(offset), loop, reader.Property(PositionName).Assign(position)); return(Expression.Block(new[] { offset, count, property }, count.Assign(reader.Call(ReadUInt16Name, null, null)), offset.Assign(reader.Call(ReadOffsetName, null, null)), property.Assign(packet.Property(prop)), property.Call(ClearName, null, null), count.NotEqual(((ushort)0).Constant()) .IfThen(read))); } BlockExpression CompileString(CompilerPacketFieldInfo info) { var offset = typeof(int).Variable("offset"); var position = typeof(int).Variable("position"); return(Expression.Block(new[] { offset, position }, offset.Assign(reader.Call(ReadOffsetName, null, null)), position.Assign(reader.Property(PositionName)), reader.Property(PositionName).Assign(offset), packet.Property(info.Property).Assign(reader.Call(ReadStringName, null, null)), reader.Property(PositionName).Assign(position))); } BlockExpression CompilePrimitive(CompilerPacketFieldInfo info) { var prop = info.Property; var ftype = prop.PropertyType; var etype = ftype.IsEnum ? ftype.GetEnumUnderlyingType() : ftype; var prefix = (info.Attribute?.IsSimpleSkill ?? false) ? SimpleName : string.Empty; Expression read = reader.Call(ReadName + prefix + etype.Name, null, null); if (ftype.IsEnum) { read = read.Convert(ftype); } return(Expression.Block(packet.Property(prop).Assign(read))); } var exprs = new List <Expression>() { packet.Assign(target.Convert(info.Type)), }; foreach (var field in info.Fields.Cast <CompilerPacketFieldInfo>()) { exprs.Add( field.IsByteArray ? CompileByteArray(field) : field.IsArray ? CompileArray(field) : field.IsString ? CompileString(field) : CompilePrimitive(field)); } return(Expression.Lambda <Action <GameBinaryReader, object> >( Expression.Block(new[] { packet }, exprs), reader, target).Compile()); }
Action <TeraBinaryWriter, object> CompileSerializer(Type type) { if (_serializers.TryGetValue(type, out var s)) { return(s); } var writer = Expression.Parameter(typeof(TeraBinaryWriter), "writer"); var source = Expression.Parameter(typeof(object), "source"); var packet = Expression.Variable(type, "packet2"); var offsets = new List <(PacketFieldInfo, ParameterExpression)>(); BlockExpression CompileByteArray1(PacketFieldInfo info) { var prop = info.Property; var offset = Expression.Variable(typeof(int), $"offset{prop.Name}"); offsets.Add((info, offset)); var property = packet.Property(prop); return(Expression.Block( offset.Assign(writer.Property(WriterPositionName)), writer.Call(WriteUInt16Name, null, new[] { ((ushort)0).Constant() }), writer.Call(WriteUInt16Name, null, new[] { property.Property(CountName).Convert(typeof(ushort)) }))); } BlockExpression CompileArray1(PacketFieldInfo info) { var prop = info.Property; var offset = Expression.Variable(typeof(int), $"offset{prop.Name}"); offsets.Add((info, offset)); var property = packet.Property(prop); return(Expression.Block( writer.Call(WriteUInt16Name, null, new[] { property.Property(CountName).Convert(typeof(ushort)) }), offset.Assign(writer.Property(WriterPositionName)), writer.Call(WriteUInt16Name, null, new[] { ((ushort)0).Constant() }))); } BlockExpression CompileString1(PacketFieldInfo info) { var offset = Expression.Variable(typeof(int), $"offset{info.Property.Name}"); offsets.Add((info, offset)); return(Expression.Block( offset.Assign(writer.Property(WriterPositionName)), writer.Call(WriteUInt16Name, null, new[] { ((ushort)0).Constant() }))); } BlockExpression CompilePrimitive(PacketFieldInfo info) { var prop = info.Property; var ftype = prop.PropertyType; var etype = ftype.IsEnum ? ftype.GetEnumUnderlyingType() : ftype; var prefix = info.Attribute.IsLocalSkill ? LocalName : string.Empty; Expression property; if (ftype == typeof(ushort) && info.Attribute.IsUnknownArray) { property = ((ushort)0).Constant(); } else { property = packet.Property(prop); if (ftype.IsEnum) { property = property.Convert(etype); } } return(Expression.Block( writer.Call(WriteName + prefix + etype.Name, null, new[] { property }))); } var exprs = new List <Expression>() { packet.Assign(source.Convert(type)) }; foreach (var info in GetPacketFields <PacketFieldInfo>(type)) { BlockExpression block; if (info.IsByteArray) { block = CompileByteArray1(info); } else if (info.IsArray) { block = CompileArray1(info); } else if (info.IsString) { block = CompileString1(info); } else { block = CompilePrimitive(info); } exprs.Add(block); } BlockExpression CompileByteArray2(PacketFieldInfo info, ParameterExpression offset) { var property = Expression.Variable(info.Property.PropertyType, "property"); var position = Expression.Variable(typeof(int), "position"); var write = Expression.Block(new[] { position }, position.Assign(writer.Property(WriterPositionName)), writer.Property(WriterPositionName).Assign(offset), writer.Call(WriteOffsetName, null, new[] { position }), writer.Property(WriterPositionName).Assign(position), writer.Call(WriteBytesName, null, new[] { property.Call(ToArrayName, null, null) })); return(Expression.Block(new[] { property }, property.Assign(packet.Property(info.Property)), property.Property(CountName).NotEqual(0.Constant()) .IfThen(write))); } BlockExpression CompileArray2(PacketFieldInfo info, ParameterExpression offset) { var prop = info.Property; var ftype = prop.PropertyType; var elemType = ftype.GetGenericArguments()[0]; var property = Expression.Variable(ftype, "property"); var count = Expression.Variable(typeof(int), "count"); var position = Expression.Variable(typeof(int), "position"); var i = Expression.Variable(typeof(int), "i"); var position2 = Expression.Variable(typeof(int), "position2"); var position3 = Expression.Variable(typeof(int), "position3"); var writeNext = Expression.Block(new[] { position3 }, position3.Assign(writer.Property(WriterPositionName)), writer.Property(WriterPositionName) .Assign(position2.Add(sizeof(ushort).Constant())), writer.Call(WriteOffsetName, null, new[] { position3 }), writer.Property(WriterPositionName).Assign(position3)); var loop = CustomExpression.For(i, 0.Constant(), i.LessThan(count), i.PostIncrementAssign(), Expression.Block(new[] { position2 }, position2.Assign(writer.Property(WriterPositionName)), writer.Call(WriteOffsetName, null, new[] { position2 }), writer.Call(WriteUInt16Name, null, new[] { ((ushort)0).Constant() }), Expression.Invoke(CompileSerializer(elemType) .Constant(), writer, property.Property(ItemName, i)), i.NotEqual(count.Subtract(1.Constant())) .IfThen(writeNext))); var write = Expression.Block(new[] { position, i }, position.Assign(writer.Property(WriterPositionName)), writer.Property(WriterPositionName).Assign(offset), writer.Call(WriteOffsetName, null, new[] { position }), writer.Property(WriterPositionName).Assign(position), loop); return(Expression.Block(new[] { property, count }, property.Assign(packet.Property(prop)), count.Assign(property.Property(CountName)), count.NotEqual(0.Constant()).IfThen(write))); } BlockExpression CompileString2(PacketFieldInfo info, ParameterExpression offset) { var position = Expression.Variable(typeof(int), "position"); return(Expression.Block(new[] { position }, position.Assign(writer.Property(WriterPositionName)), writer.Property(WriterPositionName).Assign(offset), writer.Call(WriteOffsetName, null, new[] { position }), writer.Property(WriterPositionName).Assign(position), writer.Call(WriteStringName, null, new[] { packet.Property(info.Property) }))); } foreach (var(info, offset) in offsets) { BlockExpression block; if (info.IsByteArray) { block = CompileByteArray2(info, offset); } else if (info.IsArray) { block = CompileArray2(info, offset); } else { block = CompileString2(info, offset); } exprs.Add(block); } return(Expression.Lambda <Action <TeraBinaryWriter, object> >( Expression.Block(new[] { packet }.Concat(offsets.Select(tup => tup.Item2)), exprs), writer, source).Compile()); }
Action <GameBinaryWriter, object> CompileSerializer(PacketInfo info) { if (_serializers.TryGetValue(info, out var s)) { return(s); } var writer = typeof(GameBinaryWriter).Parameter("writer"); var source = typeof(object).Parameter("source"); var packet = info.Type.Variable("packet2"); var offsets = new List <(CompilerPacketFieldInfo, ParameterExpression)>(); BlockExpression CompileByteArray1(CompilerPacketFieldInfo info) { var prop = info.Property; var offset = typeof(int).Variable($"offset{prop.Name}"); offsets.Add((info, offset)); var property = packet.Property(prop); return(Expression.Block( offset.Assign(writer.Property(PositionName)), writer.Call(WriteUInt16Name, null, new[] { ((ushort)0).Constant() }), writer.Call(WriteUInt16Name, null, new[] { property.Property(CountName).Convert(typeof(ushort)) }))); } BlockExpression CompileArray1(CompilerPacketFieldInfo info) { var prop = info.Property; var offset = typeof(int).Variable($"offset{prop.Name}"); offsets.Add((info, offset)); var property = packet.Property(prop); return(Expression.Block( writer.Call(WriteUInt16Name, null, new[] { property.Property(CountName).Convert(typeof(ushort)) }), offset.Assign(writer.Property(PositionName)), writer.Call(WriteUInt16Name, null, new[] { ((ushort)0).Constant() }))); } BlockExpression CompileString1(CompilerPacketFieldInfo info) { var offset = typeof(int).Variable($"offset{info.Property.Name}"); offsets.Add((info, offset)); return(Expression.Block( offset.Assign(writer.Property(PositionName)), writer.Call(WriteUInt16Name, null, new[] { ((ushort)0).Constant() }))); } BlockExpression CompilePrimitive(CompilerPacketFieldInfo info) { var prop = info.Property; var ftype = prop.PropertyType; var etype = ftype.IsEnum ? ftype.GetEnumUnderlyingType() : ftype; var prefix = (info.Attribute?.IsSimpleSkill ?? false) ? SimpleName : string.Empty; Expression property; if (ftype == typeof(ushort) && (info.Attribute?.IsUnknownArray ?? false)) { property = ((ushort)0).Constant(); } else { property = packet.Property(prop); if (ftype.IsEnum) { property = property.Convert(etype); } } return(Expression.Block( writer.Call(WriteName + prefix + etype.Name, null, new[] { property }))); } var exprs = new List <Expression>() { packet.Assign(source.Convert(info.Type)), }; foreach (var field in info.Fields.Cast <CompilerPacketFieldInfo>()) { exprs.Add( field.IsByteArray ? CompileByteArray1(field) : field.IsArray ? CompileArray1(field) : field.IsString ? CompileString1(field) : CompilePrimitive(field)); } BlockExpression CompileByteArray2(CompilerPacketFieldInfo info, ParameterExpression offset) { var property = info.Property.PropertyType.Variable("property"); var position = typeof(int).Variable("position"); var write = Expression.Block(new[] { position }, position.Assign(writer.Property(PositionName)), writer.Property(PositionName).Assign(offset), writer.Call(WriteOffsetName, null, new[] { position }), writer.Property(PositionName).Assign(position), writer.Call(WriteBytesName, null, new[] { property.Call(ToArrayName, null, null).Convert(typeof(ReadOnlySpan <byte>)) })); return(Expression.Block(new[] { property }, property.Assign(packet.Property(info.Property)), property.Property(CountName).NotEqual(0.Constant()) .IfThen(write))); } BlockExpression CompileArray2(CompilerPacketFieldInfo info, ParameterExpression offset) { var prop = info.Property; var ftype = prop.PropertyType; var elemInfo = GetPacketInfo(ftype.GetGenericArguments()[0]); var property = ftype.Variable("property"); var count = typeof(int).Variable("count"); var position = typeof(int).Variable("position"); var i = typeof(int).Variable("i"); var position2 = typeof(int).Variable("position2"); var position3 = typeof(int).Variable("position3"); var writeNext = Expression.Block(new[] { position3 }, position3.Assign(writer.Property(PositionName)), writer.Property(PositionName).Assign(position2.Add(sizeof(ushort).Constant())), writer.Call(WriteOffsetName, null, new[] { position3 }), writer.Property(PositionName).Assign(position3)); var loop = CustomExpression.For( i, 0.Constant(), i.LessThan(count), i.PreIncrementAssign(), Expression.Block(new[] { position2 }, position2.Assign(writer.Property(PositionName)), writer.Call(WriteOffsetName, null, new[] { position2 }), writer.Call(WriteUInt16Name, null, new[] { ((ushort)0).Constant() }), CompileSerializer(elemInfo).Constant().Invoke(writer, property.Property(ItemName, i)), i.NotEqual(count.Subtract(1.Constant())) .IfThen(writeNext))); var write = Expression.Block(new[] { position, i }, position.Assign(writer.Property(PositionName)), writer.Property(PositionName).Assign(offset), writer.Call(WriteOffsetName, null, new[] { position }), writer.Property(PositionName).Assign(position), loop); return(Expression.Block(new[] { property, count }, property.Assign(packet.Property(prop)), count.Assign(property.Property(CountName)), count.NotEqual(0.Constant()) .IfThen(write))); } BlockExpression CompileString2(CompilerPacketFieldInfo info, ParameterExpression offset) { var position = typeof(int).Variable("position"); return(Expression.Block(new[] { position }, position.Assign(writer.Property(PositionName)), writer.Property(PositionName).Assign(offset), writer.Call(WriteOffsetName, null, new[] { position }), writer.Property(PositionName).Assign(position), writer.Call(WriteStringName, null, new[] { packet.Property(info.Property) }))); } foreach (var(field, offset) in offsets) { exprs.Add( field.IsByteArray ? CompileByteArray2(field, offset) : field.IsArray ? CompileArray2(field, offset) : CompileString2(field, offset)); } var vars = new[] { packet }.Concat(offsets.Select(tup => tup.Item2)); return(Expression.Lambda <Action <GameBinaryWriter, object> >( Expression.Block(vars, exprs), writer, source).Compile()); }