using System.Numerics; using System.Text; namespace XFEEliteDangerous.NativeFormat.Replication; /// A nonzero remote factory identifier, with an optional first-use name definition. public sealed record EdServerObjectFactory(ushort Identifier, string? DefinitionName = null); /// /// The ordinary leaf-object subset of native +OBJ. References are raw U48 values; /// allocation, ownership, factory availability and live state require separate policy. /// Dependencies here are replication-session references (653900), not parent objects. /// Cloned objects and nested construction are deliberately outside this subset. /// public sealed record EdServerLeafObjectCreate(ulong ObjectReference, ulong ParentObjectReference, bool GoLiveWhenReady, EdServerObjectFactory Factory, ushort Version, IReadOnlyList Dependencies, ulong? OwnerMachineReference = null); /// /// Statically closed leaf layout from650060,64D1C0,653AB0 and53BDB0 in the pinned /// client. It describes construction only: it does not supply reflected properties, /// ServerAdmin behaviour, a world or a successful native-live transition. /// public static class EdServerObjectCreateCodec { public const byte LetterType = 17; public const int MaximumFactoryNameBytes = 128; public const int MaximumDependencies = 64; // Native sender7FBD30 maps its configured client-root MCO to signed-255; // receiver7FBCE0 reverses that mapping. The wire carries the low48 bits. public const ulong ClientRootReference = 0xFFFFFFFFFF01; private const ulong MaximumReference = 0xFFFFFFFFFFFF; public static EdServerLeafObjectCreate ReadLeafBody(ReadOnlySpan source, ref int bitOffset) { var p = bitOffset; var reference = Read(source, ref p, 48); var parent = Read(source, ref p, 48); var goLive = Read(source, ref p, 1) != 0; var identifier = Compressed(source, ref p, 4, ushort.MaxValue - 1); string? name = null; if (identifier == 0) { identifier = Compressed(source, ref p, 4, ushort.MaxValue - 1); if (identifier == 0) throw new InvalidDataException("Factory zero is a reserved definition marker."); var length = Read(source, ref p, 32); if (length is < 1 or > MaximumFactoryNameBytes) throw new InvalidDataException("Factory name exceeds the local bound."); Require(source.Length, p, checked((long)length * 8)); Span bytes = stackalloc byte[(int)length]; for (var i = 0; i < bytes.Length; i++) { bytes[i] = (byte)Read(source, ref p, 8); if (bytes[i] is < 32 or > 126) throw new InvalidDataException("Unsupported factory-name encoding."); } name = Encoding.ASCII.GetString(bytes); } var version = (ushort)Read(source, ref p, 16); if (Read(source, ref p, 1) != 0) throw new NotSupportedException("Cloned native object construction is not implemented."); var count = Compressed(source, ref p, 5, MaximumDependencies); Require(source.Length, p, count * 48L); var dependencies = new ulong[count]; for (var i = 0; i < count; i++) dependencies[i] = Read(source, ref p, 48); ulong? owner = Read(source, ref p, 1) == 0 ? null : Read(source, ref p, 48); if (Compressed(source, ref p, 5, 4096) != 0) throw new NotSupportedException("Nested native object construction is not implemented."); bitOffset = p; return new(reference, parent, goLive, new((ushort)identifier, name), version, Array.AsReadOnly(dependencies), owner); } public static int GetLeafBodyBitLength(EdServerLeafObjectCreate message) { var snapshot = Snapshot(message); return Bits(message, snapshot.Name, snapshot.Dependencies); } public static void WriteLeafBody(Span destination, ref int bitOffset, EdServerLeafObjectCreate message) { var snapshot = Snapshot(message); Require(destination.Length, bitOffset, Bits(message, snapshot.Name, snapshot.Dependencies)); var p = bitOffset; Write(destination, ref p, message.ObjectReference, 48); Write(destination, ref p, message.ParentObjectReference, 48); Write(destination, ref p, message.GoLiveWhenReady ? 1UL : 0, 1); if (snapshot.Name is not null) { WriteCompressed(destination, ref p, 0, 4); WriteCompressed(destination, ref p, message.Factory.Identifier, 4); Write(destination, ref p, (uint)snapshot.Name.Length, 32); foreach (var value in snapshot.Name) Write(destination, ref p, value, 8); } else WriteCompressed(destination, ref p, message.Factory.Identifier, 4); Write(destination, ref p, message.Version, 16); Write(destination, ref p, 0, 1); // No existing-object/clone reference. WriteCompressed(destination, ref p, snapshot.Dependencies.Length, 5); foreach (var dependency in snapshot.Dependencies) Write(destination, ref p, dependency, 48); Write(destination, ref p, message.OwnerMachineReference.HasValue ? 1UL : 0, 1); if (message.OwnerMachineReference is ulong owner) Write(destination, ref p, owner, 48); WriteCompressed(destination, ref p, 0, 5); // This subset has no nested children. bitOffset = p; } private static (byte[]? Name, ulong[] Dependencies) Snapshot(EdServerLeafObjectCreate message) { ArgumentNullException.ThrowIfNull(message); ArgumentNullException.ThrowIfNull(message.Factory); ArgumentNullException.ThrowIfNull(message.Dependencies); Reference(message.ObjectReference); Reference(message.ParentObjectReference); if (message.OwnerMachineReference.HasValue) Reference(message.OwnerMachineReference.Value); if (message.Factory.Identifier is 0 or ushort.MaxValue) throw new ArgumentOutOfRangeException(nameof(message)); byte[]? name = null; if (message.Factory.DefinitionName is string text) { if (text.Length is < 1 or > MaximumFactoryNameBytes || text.Any(value => value is < ' ' or > '~')) throw new ArgumentException("Unsupported bounded native factory name.", nameof(message)); name = Encoding.ASCII.GetBytes(text); } if (message.Dependencies.Count > MaximumDependencies) throw new ArgumentOutOfRangeException(nameof(message)); var dependencies = message.Dependencies.ToArray(); if (dependencies.Length > MaximumDependencies) throw new ArgumentOutOfRangeException(nameof(message)); foreach (var dependency in dependencies) Reference(dependency); return (name, dependencies); } private static int Bits(EdServerLeafObjectCreate message, byte[]? name, ulong[] dependencies) => 48 + 48 + 1 + CountBits(message.Factory.Identifier, 4) + (name is null ? 0 : 4 + 32 + name.Length * 8) + 16 + 1 + CountBits(dependencies.Length, 5) + dependencies.Length * 48 + 1 + (message.OwnerMachineReference.HasValue ? 48 : 0) + 5; private static int CountBits(int value, int exponentBits) => exponentBits + BitOperations.Log2((uint)value + 1); private static void Reference(ulong value) { if (value > MaximumReference) throw new ArgumentOutOfRangeException(nameof(value)); } private static int Compressed(ReadOnlySpan source, ref int p, int exponentBits, int maximum) { var exponent = (int)Read(source, ref p, exponentBits); var value = (1UL << exponent) + Read(source, ref p, exponent) - 1; if (value > (uint)maximum) throw new InvalidDataException("Compressed native value exceeds the local bound."); return (int)value; } private static void WriteCompressed(Span destination, ref int p, int value, int exponentBits) { var exponent = BitOperations.Log2((uint)value + 1); Write(destination, ref p, (uint)exponent, exponentBits); Write(destination, ref p, (ulong)value + 1 - (1UL << exponent), exponent); } private static ulong Read(ReadOnlySpan source, ref int p, int count) { Require(source.Length, p, count); ulong value = 0; for (var i = 0; i < count; i++, p++) value |= (ulong)((source[p / 8] >> (p % 8)) & 1) << i; return value; } private static void Write(Span destination, ref int p, ulong value, int count) { Require(destination.Length, p, count); for (var i = 0; i < count; i++, p++) { var mask = 1 << (p % 8); destination[p / 8] = (byte)((destination[p / 8] & ~mask) | (((value >> i) & 1) == 0 ? 0 : mask)); } } private static void Require(int byteCount, int bitOffset, long count) { if (bitOffset < 0 || count < 0 || (long)bitOffset + count > (long)byteCount * 8) throw new InvalidDataException("Native object creation is truncated or has an invalid bit offset."); } }