using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using CommandLine; using YamlDotNet.Core; using YamlDotNet.Serialization; namespace ArcToolkitCLI.Commands.Converters { [Verb("mate", HelpText = "Convert MATE files into YAML and back")] public class MateConverter : BaseConverter { private const string MATE_TAG = "CM3D2_MATERIAL"; private readonly Dictionary mappingTypes; public MateConverter() { Converters = new Dictionary> { [".mate"] = MateToYaml, [".yaml"] = YamlToMate }; mappingTypes = GetType().GetNestedTypes(BindingFlags.NonPublic).Select(t => new { type = t, attrs = t.GetCustomAttributes(typeof(TagAttribute), true).Cast().ToArray() }) .Where(d => d.attrs.Length != 0) .ToDictionary(d => $"!{d.attrs.First().Tag}", d => d.type); } private int YamlToMate(string file) { var fileName = Path.GetFileNameWithoutExtension(file); if (!fileName.ToLowerInvariant().EndsWith(".mate")) { Console.WriteLine($"Filename of {file} must have .mate.yaml extension! Skipping..."); return 0; } var db = new DeserializerBuilder(); foreach (var kv in mappingTypes) db.WithTagMapping(kv.Key, kv.Value); var deserializer = db.Build(); MateFile data; try { data = deserializer.Deserialize(new StreamReader(file, Encoding.UTF8)); } catch (YamlException e) { Console.WriteLine($"Failed to read YAML file because {e.Message}: {e.InnerException?.Message}"); return 1; } using var bw = new BinaryWriter(File.Create(Path.Combine(Output, fileName)), Encoding.UTF8); data.Serialize(bw); return 0; } private int MateToYaml(string file) { using var br = new BinaryReader(File.OpenRead(file), Encoding.UTF8); var tag = br.ReadString(); if (tag != MATE_TAG) { Console.WriteLine("Provided file is not a valid MATE file!"); return 1; } var mateFile = new MateFile { Version = br.ReadInt32(), Path = br.ReadString(), Name = br.ReadString(), ShaderName = br.ReadString(), DefMaterial = br.ReadString(), ShaderProperties = new List() }; while (true) { var prop = br.ReadString(); if (prop == "end") { mateFile.ShaderProperties.Add(new EndTag()); break; } // TODO: Clean up var paramName = br.ReadString(); ShaderVariable sVar; switch (prop) { case "tex": var texType = br.ReadString(); sVar = texType switch { "null" => new TexNullVar(), "tex2d" => new Tex2DVar { TextureName = br.ReadString(), Path = br.ReadString(), Offset = new Vec2 { X = br.ReadSingle(), Y = br.ReadSingle() }, Scale = new Vec2 { X = br.ReadSingle(), Y = br.ReadSingle() } }, "texRT" => new TexRTVar { TextureName = br.ReadString(), Path = br.ReadString() }, _ => throw new Exception($"Invalid prop {texType} in file {file}") }; break; case "col": sVar = new ColVar { Red = br.ReadSingle(), Green = br.ReadSingle(), Blue = br.ReadSingle(), Alpha = br.ReadSingle() }; break; case "vec": sVar = new Vec4Var { X = br.ReadSingle(), Y = br.ReadSingle(), Z = br.ReadSingle(), W = br.ReadSingle() }; break; case "f": sVar = new FloatVar { Value = br.ReadSingle() }; break; default: Console.WriteLine($"Invalid prop {prop} in file {file}"); return 1; } sVar.ShaderProperty = paramName; mateFile.ShaderProperties.Add(sVar); } var sb = new SerializerBuilder(); sb.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull); foreach (var kv in mappingTypes) sb.WithTagMapping(kv.Key, kv.Value); var serializer = sb.Build(); var yaml = serializer.Serialize(mateFile); File.WriteAllText(Path.Combine(Output, $"{Path.GetFileName(file)}.yaml"), yaml); return 0; } [AttributeUsage(AttributeTargets.Class, Inherited = false)] private class TagAttribute : Attribute { public TagAttribute(string Tag) { this.Tag = Tag; } public string Tag { get; } public bool WriteShaderProp { get; set; } = true; } private class ShaderVariable { [YamlMember(Alias = "property")] public string ShaderProperty { get; set; } public virtual void Serialize(BinaryWriter bw) { var tagAttribute = GetType().GetCustomAttributes(typeof(TagAttribute), true).Cast() .FirstOrDefault(); if (tagAttribute == null) return; bw.Write(tagAttribute.Tag); if (tagAttribute.WriteShaderProp) bw.Write(ShaderProperty); } } [Tag("end", WriteShaderProp = false)] private class EndTag : ShaderVariable { } private class TexVar : ShaderVariable { public override void Serialize(BinaryWriter bw) { var tagAttribute = GetType().GetCustomAttributes(typeof(TagAttribute), true).Cast() .FirstOrDefault(); if (tagAttribute == null) return; bw.Write("tex"); bw.Write(ShaderProperty); bw.Write(tagAttribute.Tag); } } [Tag("tex_null")] private class TexNullVar : TexVar { public override void Serialize(BinaryWriter bw) { bw.Write("tex"); bw.Write(ShaderProperty); bw.Write("null"); } } [Tag("tex2d")] private class Tex2DVar : TexVar { [YamlMember(Alias = "texname")] public string TextureName { get; set; } [YamlMember(Alias = "path")] public string Path { get; set; } [YamlMember(Alias = "offset")] public Vec2 Offset { get; set; } [YamlMember(Alias = "scale")] public Vec2 Scale { get; set; } public override void Serialize(BinaryWriter bw) { base.Serialize(bw); bw.Write(TextureName); bw.Write(Path); bw.Write(Offset.X); bw.Write(Offset.Y); bw.Write(Scale.X); bw.Write(Scale.Y); } } [Tag("texRT")] private class TexRTVar : TexVar { [YamlMember(Alias = "texname")] public string TextureName { get; set; } [YamlMember(Alias = "path")] public string Path { get; set; } public override void Serialize(BinaryWriter bw) { base.Serialize(bw); bw.Write(Path); bw.Write(TextureName); } } [Tag("col")] private class ColVar : ShaderVariable { [YamlMember(Alias = "r")] public float Red { get; set; } [YamlMember(Alias = "g")] public float Green { get; set; } [YamlMember(Alias = "b")] public float Blue { get; set; } [YamlMember(Alias = "a")] public float Alpha { get; set; } public override void Serialize(BinaryWriter bw) { base.Serialize(bw); bw.Write(Red); bw.Write(Green); bw.Write(Blue); bw.Write(Alpha); } } [Tag("vec")] private class Vec4Var : ShaderVariable { [YamlMember(Alias = "x")] public float X { get; set; } [YamlMember(Alias = "y")] public float Y { get; set; } [YamlMember(Alias = "z")] public float Z { get; set; } [YamlMember(Alias = "w")] public float W { get; set; } public override void Serialize(BinaryWriter bw) { base.Serialize(bw); bw.Write(X); bw.Write(Y); bw.Write(Z); bw.Write(W); } } [Tag("f")] private class FloatVar : ShaderVariable { [YamlMember(Alias = "value")] public float Value { get; set; } public override void Serialize(BinaryWriter bw) { base.Serialize(bw); bw.Write(Value); } } private class Vec2 { [YamlMember(Alias = "x")] public float X { get; set; } [YamlMember(Alias = "y")] public float Y { get; set; } } private class MateFile { [YamlMember(Alias = "version")] public int Version { get; set; } [YamlMember(Alias = "path")] public string Path { get; set; } [YamlMember(Alias = "name")] public string Name { get; set; } [YamlMember(Alias = "shader")] public string ShaderName { get; set; } [YamlMember(Alias = "def_material")] public string DefMaterial { get; set; } [YamlMember(Alias = "shader_props")] public List ShaderProperties { get; set; } public void Serialize(BinaryWriter bw) { bw.Write(MATE_TAG); bw.Write(Version); bw.Write(Path); bw.Write(Name); bw.Write(ShaderName); bw.Write(DefMaterial); foreach (var shaderProperty in ShaderProperties) shaderProperty.Serialize(bw); } } } }