using System; using System.Collections.Generic; using System.Linq; namespace ScreenConnect; public sealed class MessageSerializer { private class MessageSerializationInfo { public byte MessageID; public Type Type; public bool IsUrgent; public Constraint Constraint; public IList PropertySerializers; } private static MessageSerializer instance; private Dictionary idMap; private Dictionary typeMap; public static MessageSerializer Instance { get { if (instance == null) { lock (typeof(MessageSerializer)) { if (instance == null) { instance = new MessageSerializer(); } } } return instance; } } private MessageSerializer() { List source = (from pluginType in Extensions.GetPluginTypes() let messageAttribute = pluginType.GetCustomAttribute() where messageAttribute != null let constraint = TypeSerializer.GetConstraint(pluginType.GetCustomAttribute()) select new MessageSerializationInfo { MessageID = messageAttribute.MessageID, Type = pluginType, IsUrgent = messageAttribute.IsUrgent, Constraint = constraint, PropertySerializers = TypeSerializer.GetPropertySerializers(pluginType) }).ToList(); idMap = source.ToDictionary((MessageSerializationInfo it) => it.MessageID, (MessageSerializationInfo it) => it); typeMap = source.ToDictionary((MessageSerializationInfo it) => it.Type, (MessageSerializationInfo it) => it); } private MessageSerializationInfo DemandMessageSerializationInfo(byte messageID, Type requireBaseClass) { MessageSerializationInfo messageSerializationInfo = idMap.TryGetValue(messageID); if (messageSerializationInfo == null) { throw new InvalidOperationException("Received unrecognized message ID: 0x" + messageID.ToString("X2")); } if (!messageSerializationInfo.Type.IsOfType(requireBaseClass)) { throw new InvalidOperationException("Message must be type " + requireBaseClass?.ToString() + " but was " + messageSerializationInfo.Type); } return messageSerializationInfo; } public int? GetFixedSize(Type messageType) { int? fixedSize = TypeSerializer.GetFixedSize(typeMap[messageType].PropertySerializers); if (!fixedSize.HasValue) { return null; } return 1 + fixedSize; } public bool IsUrgent(object message) { if (message == null) { return false; } return message.GetType()?.Pipe((Type it) => typeMap[it].IsUrgent) == true; } public void Serialize(BinaryWriter writer, object message) { Type type = message.GetType(); MessageSerializationInfo messageSerializationInfo = typeMap[type]; writer.Write(messageSerializationInfo.MessageID); TypeSerializer.SerializeProperties(writer, messageSerializationInfo.PropertySerializers, message, messageSerializationInfo.Constraint); } public T Deserialize(BinaryReader reader) { return (T)Deserialize(reader, typeof(T)); } public object Deserialize(BinaryReader reader, Type requireBaseClass = null) { byte messageID = reader.ReadByte(); MessageSerializationInfo messageSerializationInfo = DemandMessageSerializationInfo(messageID, requireBaseClass); object obj = Activator.CreateInstance(messageSerializationInfo.Type); TypeSerializer.DeserializeProperties(reader, messageSerializationInfo.PropertySerializers, obj, messageSerializationInfo.Constraint); return obj; } }