using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; // CS0649: the stand-in fields are only ever assigned by BinaryFormatter via reflection. #pragma warning disable 0649 namespace SiteConfigMerge { // Stand-ins for the TeslaConsole types serialized into .siteconfig files. // Field names/types MUST match TeslaSuite\Console\TeslaConsole\{Squad,Pod,HostType}.cs // exactly ([NonSerialized] members omitted); BinaryFormatter matches by field name. [Serializable] internal class Squad { private Guid mGuid = Guid.NewGuid(); private string mName = ""; private bool mOnline; public Guid Guid => mGuid; public bool Online => mOnline; public string Name { get => mName; set => mName = value; } } [Serializable] internal class Pod { private Guid mId = Guid.NewGuid(); private IPAddress mIPAddress = IPAddress.Any; private IPAddress mGateway = IPAddress.Any; private IPAddress mDns = IPAddress.Any; private IPAddress mSubnet = IPAddress.Any; private string mHostName = ""; private byte[] mKey; private byte[] mMacAddress; private string mName; private string mPodArtPath; private HostType mHostType; private bool mOnline; public Guid Id => mId; public IPAddress IPAddress => mIPAddress; public IPAddress Gateway => mGateway; public IPAddress Dns => mDns; public IPAddress Subnet => mSubnet; public string HostName => mHostName; public byte[] Key => mKey; public byte[] MacAddress => mMacAddress; public string Name => mName; public string PodArtPath => mPodArtPath; public HostType HostType => mHostType; public bool Online => mOnline; } internal enum HostType { GameMachineHostType = 0, MissionReviewHostType = 2, ConsoleHostType = 3, } // Maps "TeslaConsole.*" stream types to the stand-ins on read, and writes them // back out under the original TeslaConsole assembly identity so the console // accepts the result. The assembly string is captured from the first input file // (falling back to the 4.11.4.1 identity). internal sealed class TeslaBinder : SerializationBinder { public const string DefaultAssembly = "TeslaConsole, Version=4.11.4.1, Culture=neutral, PublicKeyToken=null"; public string CapturedAssembly { get; private set; } public override Type BindToType(string assemblyName, string typeName) { switch (typeName) { case "TeslaConsole.Squad": CapturedAssembly = CapturedAssembly ?? assemblyName; return typeof(Squad); case "TeslaConsole.Pod": CapturedAssembly = CapturedAssembly ?? assemblyName; return typeof(Pod); case "TeslaConsole.HostType": return typeof(HostType); default: return null; // system types resolve normally } } public override void BindToName(Type serializedType, out string assemblyName, out string typeName) { assemblyName = null; typeName = null; if (serializedType == typeof(Squad)) typeName = "TeslaConsole.Squad"; else if (serializedType == typeof(Pod)) typeName = "TeslaConsole.Pod"; else if (serializedType == typeof(HostType)) typeName = "TeslaConsole.HostType"; if (typeName != null) assemblyName = CapturedAssembly ?? DefaultAssembly; } } internal sealed class PodEntry { public Pod Pod; public byte[] Raw; // the pod's original BinaryFormatter payload, copied verbatim on write } internal sealed class SquadEntry { public Squad Squad; public List Pods = new List(); } // .siteconfig container format, per TeslaSuite\Console\TeslaConsole\Site.cs: // int32 squadCount // per squad: BinaryFormatter(Squad), int32 podCount, podCount x BinaryFormatter(Pod) internal static class SiteConfigFile { public static List Read(string path, TeslaBinder binder) { byte[] bytes = File.ReadAllBytes(path); var stream = new MemoryStream(bytes, writable: false); var reader = new BinaryReader(stream); var formatter = new BinaryFormatter { Binder = binder }; var squads = new List(); int squadCount = reader.ReadInt32(); for (int i = 0; i < squadCount; i++) { var entry = new SquadEntry { Squad = (Squad)formatter.Deserialize(stream) }; int podCount = reader.ReadInt32(); for (int j = 0; j < podCount; j++) { long start = stream.Position; var pod = (Pod)formatter.Deserialize(stream); var raw = new byte[stream.Position - start]; Array.Copy(bytes, start, raw, 0, raw.Length); entry.Pods.Add(new PodEntry { Pod = pod, Raw = raw }); } squads.Add(entry); } if (stream.Position != stream.Length) Console.Error.WriteLine( $"WARNING: {path}: {stream.Length - stream.Position} trailing byte(s) after the last squad were ignored."); return squads; } public static void Write(string path, List squads, TeslaBinder binder) { using (var stream = File.Create(path)) { var writer = new BinaryWriter(stream); var formatter = new BinaryFormatter { Binder = binder }; writer.Write(squads.Count); foreach (var entry in squads) { formatter.Serialize(stream, entry.Squad); writer.Write(entry.Pods.Count); foreach (var pod in entry.Pods) stream.Write(pod.Raw, 0, pod.Raw.Length); } } } } }