csharp/0x0ade/CelesteNet/CelesteNet.Shared/YamlHelper.cs

YamlHelper.cs
using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.ObjectFactories;

namespace Celeste.Mod.CelesteNet {
    // Based off of Everest's YamlHelper.
    public static clast YamlHelper {

        public static IDeserializer Deserializer = new DeserializerBuilder()
            .IgnoreUnmatchedProperties()
            .WithTypeConverter(new ColorYamlTypeConverter())
            .Build();

        public static ISerializer Serializer = new SerializerBuilder()
            .ConfigureDefaultValuesHandling(DefaultValuesHandling.Preserve)
            .WithTypeConverter(new ColorYamlTypeConverter())
            .Build();

        /// 
        /// Builds a deserializer that will provide YamlDotNet with the given object instead of creating a new one.
        /// This will make YamlDotNet update this object when deserializing.
        /// 
        /// The object to set fields on
        /// The newly-created deserializer
        public static IDeserializer DeserializerUsing(object objectToBind) {
            IObjectFactory defaultObjectFactory = new DefaultObjectFactory();
            Type objectType = objectToBind.GetType();

            return new DeserializerBuilder()
                .IgnoreUnmatchedProperties()
                // provide the given object if type matches, fall back to default behavior otherwise.
                .WithObjectFactory(type => type == objectType ? objectToBind : defaultObjectFactory.Create(type))
                .WithTypeConverter(new ColorYamlTypeConverter())
                .Build();
        }

    }

    public clast ColorYamlTypeConverter : IYamlTypeConverter {

        public bool Accepts(Type type)
            => type == typeof(Color);

        public object ReadYaml(IParser parser, Type type) {
            if (parser.TryConsume(out MappingStart _)) {
                Color c = new(0, 0, 0, 255);
                do {
                    switch (parser.Consume().Value.ToUpperInvariant()) {
                        case "R":
                            c.R = byte.Parse(parser.Consume().Value);
                            break;

                        case "G":
                            c.G = byte.Parse(parser.Consume().Value);
                            break;

                        case "B":
                            c.B = byte.Parse(parser.Consume().Value);
                            break;

                        case "A":
                            c.A = byte.Parse(parser.Consume().Value);
                            break;
                    }
                } while (!parser.TryConsume(out MappingEnd _));
                return c;
            }

            return Calc.HexToColor(parser.Consume().Value);
        }

        public void WriteYaml(IEmitter emitter, object? value, Type type) {
            Color c = (Color) (value ?? Color.White);

            if (c.A < 255) {
                emitter.Emit(new MappingStart());
                emitter.Emit(new Scalar("R"));
                emitter.Emit(new Scalar(c.R.ToString()));
                emitter.Emit(new Scalar("G"));
                emitter.Emit(new Scalar(c.G.ToString()));
                emitter.Emit(new Scalar("B"));
                emitter.Emit(new Scalar(c.B.ToString()));
                emitter.Emit(new Scalar("A"));
                emitter.Emit(new Scalar(c.A.ToString()));
                emitter.Emit(new MappingEnd());
                return;
            }

            emitter.Emit(new Scalar($"{c.R:X2}{c.G:X2}{c.B:X2}"));
        }

    }
}