2021-08-04 00:40:09 +02:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.IO.Compression;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
|
using System.Text;
|
2021-08-04 10:11:07 +02:00
|
|
|
|
using WTelegram;
|
2021-08-04 00:40:09 +02:00
|
|
|
|
|
|
|
|
|
|
namespace TL
|
|
|
|
|
|
{
|
|
|
|
|
|
public interface ITLObject { }
|
2021-08-12 12:37:56 +02:00
|
|
|
|
public delegate string ITLFunction(BinaryWriter writer);
|
2021-08-04 00:40:09 +02:00
|
|
|
|
|
2021-08-06 20:17:19 +02:00
|
|
|
|
public static partial class Schema
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
2021-08-09 11:41:50 +02:00
|
|
|
|
internal static byte[] Serialize(this ITLObject msg)
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
|
|
|
|
|
using var memStream = new MemoryStream(1024);
|
|
|
|
|
|
using (var writer = new BinaryWriter(memStream))
|
2021-08-09 11:41:50 +02:00
|
|
|
|
WriteTLObject(writer, msg);
|
2021-08-04 00:40:09 +02:00
|
|
|
|
return memStream.ToArray();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-04 10:11:07 +02:00
|
|
|
|
internal static T Deserialize<T>(byte[] bytes) where T : ITLObject
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
|
|
|
|
|
using var memStream = new MemoryStream(bytes);
|
|
|
|
|
|
using var reader = new BinaryReader(memStream);
|
2021-08-09 11:41:50 +02:00
|
|
|
|
return (T)reader.ReadTLObject();
|
2021-08-04 00:40:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 11:41:50 +02:00
|
|
|
|
internal static void WriteTLObject(this BinaryWriter writer, ITLObject obj)
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
2021-08-09 11:41:50 +02:00
|
|
|
|
if (obj == null) { writer.Write(NullCtor); return; }
|
|
|
|
|
|
var type = obj.GetType();
|
2021-08-04 00:40:09 +02:00
|
|
|
|
var ctorNb = type.GetCustomAttribute<TLDefAttribute>().CtorNb;
|
|
|
|
|
|
writer.Write(ctorNb);
|
|
|
|
|
|
var fields = obj.GetType().GetFields().GroupBy(f => f.DeclaringType).Reverse().SelectMany(g => g);
|
|
|
|
|
|
int flags = 0;
|
|
|
|
|
|
IfFlagAttribute ifFlag;
|
|
|
|
|
|
foreach (var field in fields)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (((ifFlag = field.GetCustomAttribute<IfFlagAttribute>()) != null) && (flags & (1 << ifFlag.Bit)) == 0) continue;
|
|
|
|
|
|
object value = field.GetValue(obj);
|
2021-08-07 06:23:13 +02:00
|
|
|
|
if (value == null)
|
2021-08-09 11:41:50 +02:00
|
|
|
|
writer.WriteTLNull(field.FieldType);
|
2021-08-07 06:23:13 +02:00
|
|
|
|
else
|
2021-08-09 11:41:50 +02:00
|
|
|
|
writer.WriteTLValue(value);
|
2021-08-04 00:40:09 +02:00
|
|
|
|
if (field.Name.Equals("Flags", StringComparison.OrdinalIgnoreCase)) flags = (int)value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-12 12:37:56 +02:00
|
|
|
|
internal static ITLObject ReadTLObject(this BinaryReader reader, Func<Type, bool> customRead = null)
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
2021-08-09 11:41:50 +02:00
|
|
|
|
var ctorNb = reader.ReadUInt32();
|
|
|
|
|
|
if (ctorNb == NullCtor) return null;
|
|
|
|
|
|
if (!Table.TryGetValue(ctorNb, out var type))
|
|
|
|
|
|
throw new ApplicationException($"Cannot find type for ctor #{ctorNb:x}");
|
2021-08-04 00:40:09 +02:00
|
|
|
|
var obj = Activator.CreateInstance(type);
|
2021-08-12 12:37:56 +02:00
|
|
|
|
if (customRead?.Invoke(type) == true) return (ITLObject)obj;
|
2021-08-04 00:40:09 +02:00
|
|
|
|
var fields = obj.GetType().GetFields().GroupBy(f => f.DeclaringType).Reverse().SelectMany(g => g);
|
|
|
|
|
|
int flags = 0;
|
|
|
|
|
|
IfFlagAttribute ifFlag;
|
|
|
|
|
|
foreach (var field in fields)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (((ifFlag = field.GetCustomAttribute<IfFlagAttribute>()) != null) && (flags & (1 << ifFlag.Bit)) == 0) continue;
|
2021-08-09 11:41:50 +02:00
|
|
|
|
object value = reader.ReadTLValue(field.FieldType);
|
2021-08-04 00:40:09 +02:00
|
|
|
|
field.SetValue(obj, value);
|
|
|
|
|
|
if (field.Name.Equals("Flags", StringComparison.OrdinalIgnoreCase)) flags = (int)value;
|
|
|
|
|
|
}
|
2021-08-04 10:11:07 +02:00
|
|
|
|
return type == typeof(GzipPacked) ? UnzipPacket((GzipPacked)obj) : (ITLObject)obj;
|
2021-08-04 00:40:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 11:41:50 +02:00
|
|
|
|
internal static void WriteTLValue(this BinaryWriter writer, object value)
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
|
|
|
|
|
var type = value.GetType();
|
|
|
|
|
|
switch (Type.GetTypeCode(type))
|
|
|
|
|
|
{
|
|
|
|
|
|
case TypeCode.Int32: writer.Write((int)value); break;
|
|
|
|
|
|
case TypeCode.UInt32: writer.Write((uint)value); break;
|
|
|
|
|
|
case TypeCode.Int64: writer.Write((long)value); break;
|
|
|
|
|
|
case TypeCode.UInt64: writer.Write((ulong)value); break;
|
|
|
|
|
|
case TypeCode.Double: writer.Write((double)value); break;
|
2021-08-09 11:41:50 +02:00
|
|
|
|
case TypeCode.String: writer.WriteTLString((string)value); break;
|
|
|
|
|
|
case TypeCode.DateTime: writer.WriteTLStamp((DateTime)value); break;
|
|
|
|
|
|
case TypeCode.Boolean: writer.Write((bool)value ? 0x997275B5 : 0xBC799737); break;
|
2021-08-04 00:40:09 +02:00
|
|
|
|
case TypeCode.Object:
|
|
|
|
|
|
if (type.IsArray)
|
|
|
|
|
|
if (value is byte[] bytes)
|
2021-08-09 11:41:50 +02:00
|
|
|
|
writer.WriteTLBytes(bytes);
|
2021-08-04 00:40:09 +02:00
|
|
|
|
else
|
2021-08-09 11:41:50 +02:00
|
|
|
|
writer.WriteTLVector((Array)value);
|
2021-08-04 00:40:09 +02:00
|
|
|
|
else if (value is Int128 int128)
|
|
|
|
|
|
writer.Write(int128);
|
|
|
|
|
|
else if (value is Int256 int256)
|
|
|
|
|
|
writer.Write(int256);
|
|
|
|
|
|
else if (value is ITLObject tlObject)
|
2021-08-09 11:41:50 +02:00
|
|
|
|
WriteTLObject(writer, tlObject);
|
2021-08-04 00:40:09 +02:00
|
|
|
|
else
|
|
|
|
|
|
ShouldntBeHere();
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
ShouldntBeHere();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 11:41:50 +02:00
|
|
|
|
internal static object ReadTLValue(this BinaryReader reader, Type type)
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
|
|
|
|
|
switch (Type.GetTypeCode(type))
|
|
|
|
|
|
{
|
|
|
|
|
|
case TypeCode.Int32: return reader.ReadInt32();
|
|
|
|
|
|
case TypeCode.UInt32: return reader.ReadUInt32();
|
|
|
|
|
|
case TypeCode.Int64: return reader.ReadInt64();
|
|
|
|
|
|
case TypeCode.UInt64: return reader.ReadUInt64();
|
|
|
|
|
|
case TypeCode.Double: return reader.ReadDouble();
|
2021-08-09 11:41:50 +02:00
|
|
|
|
case TypeCode.String: return reader.ReadTLString();
|
|
|
|
|
|
case TypeCode.DateTime: return reader.ReadTLStamp();
|
2021-08-04 00:40:09 +02:00
|
|
|
|
case TypeCode.Boolean:
|
|
|
|
|
|
return reader.ReadUInt32() switch
|
|
|
|
|
|
{
|
|
|
|
|
|
0x997275b5 => true,
|
|
|
|
|
|
0xbc799737 => false,
|
|
|
|
|
|
var value => throw new ApplicationException($"Invalid boolean value #{value:x}")
|
|
|
|
|
|
};
|
|
|
|
|
|
case TypeCode.Object:
|
|
|
|
|
|
if (type.IsArray)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (type == typeof(byte[]))
|
2021-08-09 11:41:50 +02:00
|
|
|
|
return reader.ReadTLBytes();
|
2021-08-04 00:40:09 +02:00
|
|
|
|
else if (type == typeof(_Message[]))
|
2021-08-09 11:41:50 +02:00
|
|
|
|
return reader.ReadTLMessages();
|
2021-08-04 00:40:09 +02:00
|
|
|
|
else
|
2021-08-09 11:41:50 +02:00
|
|
|
|
return reader.ReadTLVector(type);
|
2021-08-04 00:40:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
else if (type == typeof(Int128))
|
|
|
|
|
|
return new Int128(reader);
|
|
|
|
|
|
else if (type == typeof(Int256))
|
|
|
|
|
|
return new Int256(reader);
|
|
|
|
|
|
else
|
2021-08-09 11:41:50 +02:00
|
|
|
|
return ReadTLObject(reader);
|
2021-08-04 00:40:09 +02:00
|
|
|
|
default:
|
|
|
|
|
|
ShouldntBeHere();
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 11:41:50 +02:00
|
|
|
|
internal static void WriteTLVector(this BinaryWriter writer, Array array)
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
|
|
|
|
|
writer.Write(VectorCtor);
|
2021-08-09 11:41:50 +02:00
|
|
|
|
if (array == null) { writer.Write(0); return; }
|
2021-08-04 00:40:09 +02:00
|
|
|
|
int count = array.Length;
|
|
|
|
|
|
writer.Write(count);
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
2021-08-09 11:41:50 +02:00
|
|
|
|
writer.WriteTLValue(array.GetValue(i));
|
2021-08-04 00:40:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 11:41:50 +02:00
|
|
|
|
internal static Array ReadTLVector(this BinaryReader reader, Type type)
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
|
|
|
|
|
var ctorNb = reader.ReadInt32();
|
|
|
|
|
|
if (ctorNb != VectorCtor) throw new ApplicationException($"Cannot deserialize {type.Name} with ctor #{ctorNb:x}");
|
|
|
|
|
|
var elementType = type.GetElementType();
|
|
|
|
|
|
int count = reader.ReadInt32();
|
|
|
|
|
|
Array array = (Array)Activator.CreateInstance(type, count);
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
2021-08-09 11:41:50 +02:00
|
|
|
|
array.SetValue(reader.ReadTLValue(elementType), i);
|
2021-08-04 00:40:09 +02:00
|
|
|
|
return array;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 11:41:50 +02:00
|
|
|
|
internal static void WriteTLStamp(this BinaryWriter writer, DateTime datetime)
|
|
|
|
|
|
=> writer.Write((uint)(datetime.ToUniversalTime().Ticks / 10000000 - 62135596800L));
|
|
|
|
|
|
|
|
|
|
|
|
internal static DateTime ReadTLStamp(this BinaryReader reader)
|
|
|
|
|
|
=> new((reader.ReadUInt32() + 62135596800L) * 10000000, DateTimeKind.Utc);
|
|
|
|
|
|
|
|
|
|
|
|
internal static void WriteTLString(this BinaryWriter writer, string str)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (str == null)
|
|
|
|
|
|
writer.Write(0);
|
|
|
|
|
|
else
|
|
|
|
|
|
writer.WriteTLBytes(Encoding.UTF8.GetBytes(str));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal static string ReadTLString(this BinaryReader reader)
|
|
|
|
|
|
=> Encoding.UTF8.GetString(reader.ReadTLBytes());
|
|
|
|
|
|
|
|
|
|
|
|
internal static void WriteTLBytes(this BinaryWriter writer, byte[] bytes)
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
2021-08-09 11:41:50 +02:00
|
|
|
|
if (bytes == null) { writer.Write(0); return; }
|
2021-08-04 00:40:09 +02:00
|
|
|
|
int length = bytes.Length;
|
|
|
|
|
|
if (length < 254)
|
|
|
|
|
|
writer.Write((byte)length);
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2021-08-06 01:54:29 +02:00
|
|
|
|
writer.Write(length << 8 | 254);
|
2021-08-04 00:40:09 +02:00
|
|
|
|
length += 3;
|
|
|
|
|
|
}
|
|
|
|
|
|
writer.Write(bytes);
|
|
|
|
|
|
while (++length % 4 != 0) writer.Write((byte)0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 11:41:50 +02:00
|
|
|
|
internal static byte[] ReadTLBytes(this BinaryReader reader)
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
|
|
|
|
|
byte[] bytes;
|
|
|
|
|
|
int length = reader.ReadByte();
|
|
|
|
|
|
if (length < 254)
|
|
|
|
|
|
bytes = reader.ReadBytes(length);
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
length = reader.ReadInt16() + (reader.ReadByte() << 16);
|
|
|
|
|
|
bytes = reader.ReadBytes(length);
|
|
|
|
|
|
length += 3;
|
|
|
|
|
|
}
|
|
|
|
|
|
while (++length % 4 != 0) reader.ReadByte();
|
|
|
|
|
|
return bytes;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 11:41:50 +02:00
|
|
|
|
internal static void WriteTLNull(this BinaryWriter writer, Type type)
|
2021-08-07 06:23:13 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (!type.IsArray)
|
|
|
|
|
|
writer.Write(NullCtor);
|
2021-08-07 06:59:36 +02:00
|
|
|
|
else if (type != typeof(byte[]))
|
2021-08-07 06:23:13 +02:00
|
|
|
|
writer.Write(VectorCtor);
|
2021-08-07 06:59:36 +02:00
|
|
|
|
writer.Write(0); // null arrays are serialized as empty
|
2021-08-07 06:23:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 11:41:50 +02:00
|
|
|
|
internal static _Message[] ReadTLMessages(this BinaryReader reader)
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
|
|
|
|
|
int count = reader.ReadInt32();
|
|
|
|
|
|
var array = new _Message[count];
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
array[i] = new _Message
|
|
|
|
|
|
{
|
|
|
|
|
|
msg_id = reader.ReadInt64(),
|
|
|
|
|
|
seqno = reader.ReadInt32(),
|
|
|
|
|
|
bytes = reader.ReadInt32(),
|
|
|
|
|
|
};
|
|
|
|
|
|
var pos = reader.BaseStream.Position;
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2021-08-09 11:41:50 +02:00
|
|
|
|
array[i].body = reader.ReadTLObject();
|
2021-08-04 00:40:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2021-08-04 10:11:07 +02:00
|
|
|
|
Helpers.Log(4, ex.ToString());
|
2021-08-04 00:40:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
reader.BaseStream.Position = pos + array[i].bytes;
|
|
|
|
|
|
}
|
|
|
|
|
|
return array;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 11:41:50 +02:00
|
|
|
|
internal static ITLObject UnzipPacket(GzipPacked obj)
|
2021-08-04 00:40:09 +02:00
|
|
|
|
{
|
|
|
|
|
|
using var reader = new BinaryReader(new GZipStream(new MemoryStream(obj.packed_data), CompressionMode.Decompress));
|
2021-08-09 11:41:50 +02:00
|
|
|
|
var result = ReadTLObject(reader);
|
2021-08-04 10:11:07 +02:00
|
|
|
|
Helpers.Log(1, $" → {result.GetType().Name}");
|
2021-08-04 00:40:09 +02:00
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
|
|
private static void ShouldntBeHere() => System.Diagnostics.Debugger.Break();
|
|
|
|
|
|
#else
|
|
|
|
|
|
private static void ShouldntBeHere() => throw new NotImplementedException("You've reached an unexpected point in code");
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[AttributeUsage(AttributeTargets.Class)]
|
|
|
|
|
|
public class TLDefAttribute : Attribute
|
|
|
|
|
|
{
|
|
|
|
|
|
public readonly uint CtorNb;
|
2021-08-06 20:17:19 +02:00
|
|
|
|
public TLDefAttribute(uint ctorNb) => CtorNb = ctorNb;
|
2021-08-04 00:40:09 +02:00
|
|
|
|
/*public TLDefAttribute(string def)
|
|
|
|
|
|
{
|
|
|
|
|
|
var hash = def.IndexOfAny(new[] { '#', ' ' });
|
|
|
|
|
|
CtorNb = def[hash] == ' ' ? Force.Crc32.Crc32Algorithm.Compute(System.Text.Encoding.UTF8.GetBytes(def))
|
|
|
|
|
|
: uint.Parse(def[(hash + 1)..def.IndexOf(' ', hash)], System.Globalization.NumberStyles.HexNumber);
|
|
|
|
|
|
}*/
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[AttributeUsage(AttributeTargets.Field)]
|
|
|
|
|
|
public class IfFlagAttribute : Attribute
|
|
|
|
|
|
{
|
|
|
|
|
|
public readonly int Bit;
|
|
|
|
|
|
public IfFlagAttribute(int bit) => Bit = bit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public struct Int128
|
|
|
|
|
|
{
|
|
|
|
|
|
public byte[] raw;
|
|
|
|
|
|
|
|
|
|
|
|
public Int128(BinaryReader reader) => raw = reader.ReadBytes(16);
|
|
|
|
|
|
public Int128(RNGCryptoServiceProvider rng) => rng.GetBytes(raw = new byte[16]);
|
|
|
|
|
|
public static bool operator ==(Int128 left, Int128 right) { for (int i = 0; i < 16; i++) if (left.raw[i] != right.raw[i]) return false; return true; }
|
|
|
|
|
|
public static bool operator !=(Int128 left, Int128 right) { for (int i = 0; i < 16; i++) if (left.raw[i] != right.raw[i]) return true; return false; }
|
|
|
|
|
|
public override bool Equals(object obj) => obj is Int128 other && this == other;
|
|
|
|
|
|
public override int GetHashCode() => HashCode.Combine(raw[0], raw[1]);
|
|
|
|
|
|
public static implicit operator byte[](Int128 int128) => int128.raw;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public struct Int256
|
|
|
|
|
|
{
|
|
|
|
|
|
public byte[] raw;
|
|
|
|
|
|
|
|
|
|
|
|
public Int256(BinaryReader reader) => raw = reader.ReadBytes(32);
|
|
|
|
|
|
public Int256(RNGCryptoServiceProvider rng) => rng.GetBytes(raw = new byte[32]);
|
|
|
|
|
|
public static bool operator ==(Int256 left, Int256 right) { for (int i = 0; i < 32; i++) if (left.raw[i] != right.raw[i]) return false; return true; }
|
|
|
|
|
|
public static bool operator !=(Int256 left, Int256 right) { for (int i = 0; i < 32; i++) if (left.raw[i] != right.raw[i]) return true; return false; }
|
|
|
|
|
|
public override bool Equals(object obj) => obj is Int256 other && this == other;
|
|
|
|
|
|
public override int GetHashCode() => HashCode.Combine(raw[0], raw[1]);
|
|
|
|
|
|
public static implicit operator byte[](Int256 int256) => int256.raw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|