using System.Text.Json; using System.Text.Json.Serialization; using SVSim.Database.Enums; namespace SVSim.EmulatedEntrypoint.Extensions; /// /// Bridges the server's internal enum (a verbatim copy of the client's /// Wizard.Format) and the wire deck_format integer the client speaks. The two are not /// interchangeable: the wire value is what flows over the network and what the client's /// LoadDetail._userRank dictionary is keyed by AFTER routing through Data.ParseApiFormat; /// the internal value is what the server uses for switches, dictionary keys, and database /// columns. /// /// Mapping mirrors the client's Wizard.Data.FormatConvertApi /// (Shadowverse_Code/Assembly-CSharp/Wizard/Data.cs:580); the inverse mirrors /// Data.ParseApiFormat (Data.cs:635). See /// docs/api-spec/common/types.ts.md for the table and rationale. /// public static class FormatExtensions { /// Internal → wire deck_format integer. public static int ToApi(this Format format) => format switch { Format.Rotation => 1, Format.Unlimited => 2, Format.Max => 1, // client sentinel; aliases onto Rotation, same as FormatConvertApi. Format.PreRotation => 3, Format.Sealed => 20, Format.MyRotation => 5, Format.TwoPick => 10, Format.Hof => 31, Format.Windfall => 33, Format.Avatar => 39, Format.All => 0, Format.Crossover => 4, _ => throw new ArgumentOutOfRangeException(nameof(format), format, $"No wire deck_format mapping for {format} ({(int)format}). " + "Update FormatExtensions.ToApi and Data.cs:580 if a new format was added.") }; /// Wire deck_format integer → internal . public static Format FromApi(int apiValue) => apiValue switch { 0 => Format.All, // Client emits 0 only for "all formats" meta-queries. 1 => Format.Rotation, 2 => Format.Unlimited, 3 => Format.PreRotation, 4 => Format.Crossover, 5 => Format.MyRotation, 10 => Format.TwoPick, 20 => Format.Sealed, 31 => Format.Hof, 33 => Format.Windfall, 39 => Format.Avatar, _ => throw new ArgumentOutOfRangeException(nameof(apiValue), apiValue, $"Unknown wire deck_format {apiValue}. The client's ParseApiFormat would warn and " + "fall back to Format.Max; we throw so the calling controller surfaces the bad input.") }; } /// /// System.Text.Json converter that emits / accepts as the wire /// deck_format integer rather than the underlying enum value. Wired up in Program.cs /// via AddJsonOptions; applies to every response DTO property typed . /// /// IMPORTANT: this only runs on the System.Text.Json serialization path (response writer + /// model binder). MessagePack-CSharp deserialization of request DTOs does NOT honor STJ /// converters — keep request DTO format fields typed as int and call /// in the controller. /// public sealed class FormatJsonConverter : JsonConverter { public override Format Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.Number || !reader.TryGetInt32(out int wire)) { throw new JsonException( $"Expected deck_format as a JSON number, got {reader.TokenType}."); } return FormatExtensions.FromApi(wire); } public override void Write(Utf8JsonWriter writer, Format value, JsonSerializerOptions options) { writer.WriteNumberValue(value.ToApi()); } }