示例#1
0
        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());
        }
示例#3
0
        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());
        }