From 1f27b25b73f28d6bb8d2107f64ee8cb977b306d5 Mon Sep 17 00:00:00 2001 From: freezy Date: Mon, 23 Dec 2019 01:16:01 +0100 Subject: [PATCH] io: Refactor Biff attributes. --- .editorconfig | 4 + .../IO/BiffAttributeTest.cs | 129 ++++++++++ .../VPT/Table/TableDataTests.cs | 27 +- .../VisualPinball.Engine.Test.csproj | 1 + VisualPinball.Engine/IO/BiffAttribute.cs | 126 +-------- VisualPinball.Engine/IO/BiffBoolAttribute.cs | 27 ++ VisualPinball.Engine/IO/BiffColorAttribute.cs | 32 +++ VisualPinball.Engine/IO/BiffFloatAttribute.cs | 49 ++++ VisualPinball.Engine/IO/BiffIntAttribute.cs | 40 +++ .../IO/BiffStringAttribute.cs | 46 ++++ .../IO/BiffVertexAttribute.cs | 20 ++ VisualPinball.Engine/VPT/ItemData.cs | 2 +- VisualPinball.Engine/VPT/Material.cs | 6 +- .../VPT/Primitive/PrimitiveData.cs | 104 ++++---- VisualPinball.Engine/VPT/Table/TableData.cs | 243 +++++++++--------- .../VisualPinball.Engine.csproj | 7 + .../VisualPinball.Unity.csproj | 1 + 17 files changed, 553 insertions(+), 311 deletions(-) create mode 100644 .editorconfig create mode 100644 VisualPinball.Engine.Test/IO/BiffAttributeTest.cs create mode 100644 VisualPinball.Engine/IO/BiffBoolAttribute.cs create mode 100644 VisualPinball.Engine/IO/BiffColorAttribute.cs create mode 100644 VisualPinball.Engine/IO/BiffFloatAttribute.cs create mode 100644 VisualPinball.Engine/IO/BiffIntAttribute.cs create mode 100644 VisualPinball.Engine/IO/BiffStringAttribute.cs create mode 100644 VisualPinball.Engine/IO/BiffVertexAttribute.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..7fbd8e360 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*] +end_of_line = crlf +insert_final_newline = true +indent_style = tab diff --git a/VisualPinball.Engine.Test/IO/BiffAttributeTest.cs b/VisualPinball.Engine.Test/IO/BiffAttributeTest.cs new file mode 100644 index 000000000..082cda62f --- /dev/null +++ b/VisualPinball.Engine.Test/IO/BiffAttributeTest.cs @@ -0,0 +1,129 @@ +using System; +using System.Linq; +using System.Reflection; +using VisualPinball.Engine.IO; +using VisualPinball.Engine.Math; +using Xunit; + +namespace VisualPinball.Engine.Test.IO +{ + public class BiffAttributeTest + { + [Fact] + public void ShouldNotUseCountAndIndex() + { + GetAttributes(typeof(BiffAttribute), (memberType, member, biffDataType, attr) => + { + if (attr.Count > -1 && attr.Index > -1) { + throw new Exception($"Must use either Count or Index but not both at {biffDataType.FullName}.{member.Name} ({attr.Name})."); + } + }); + } + + [Fact] + public void ShouldBeAppliedToStrings() + { + GetAttributes(typeof(BiffStringAttribute), (memberType, member, biffDataType, attr) => + { + if (memberType != typeof(string) && memberType != typeof(string[])) { + throw new Exception($"BiffString of {biffDataType.FullName}.{member.Name} ({attr.Name}) must be either string or string[], but is {memberType.Name}."); + } + }); + } + + [Fact] + public void ShouldBeAppliedToIntegers() + { + GetAttributes(typeof(BiffIntAttribute), (memberType, member, biffDataType, attr) => + { + if (memberType != typeof(int) && memberType != typeof(int[])) { + throw new Exception($"BiffInt of {biffDataType.FullName}.{member.Name} ({attr.Name}) must be either int or int[], but is {memberType.Name}."); + } + }); + } + + [Fact] + public void ShouldBeAppliedToFloats() + { + GetAttributes(typeof(BiffFloatAttribute), (memberType, member, biffDataType, attr) => + { + if (memberType != typeof(float) && memberType != typeof(float[])) { + throw new Exception($"BiffFloat of {biffDataType.FullName}.{member.Name} ({attr.Name}) must be either float or float[], but is {memberType.Name}."); + } + }); + } + + [Fact] + public void ShouldBeAppliedToBooleans() + { + GetAttributes(typeof(BiffBoolAttribute), (memberType, member, biffDataType, attr) => + { + if (memberType != typeof(bool) && memberType != typeof(bool[])) { + throw new Exception($"BiffBool of {biffDataType.FullName}.{member.Name} ({attr.Name}) must be either bool or bool[], but is {memberType.Name}."); + } + }); + } + + [Fact] + public void ShouldBeAppliedToColors() + { + GetAttributes(typeof(BiffColorAttribute), (memberType, member, biffDataType, attr) => + { + if (memberType != typeof(Color) && memberType != typeof(Color[])) { + throw new Exception($"BiffColor of {biffDataType.FullName}.{member.Name} ({attr.Name}) must be either Color or Color[], but is {memberType.Name}."); + } + }); + } + + [Fact] + public void ShouldBeAppliedToVertices() + { + GetAttributes(typeof(BiffVertexAttribute), (memberType, member, biffDataType, attr) => + { + if (memberType != typeof(Vertex2D) && memberType != typeof(Vertex3D)) { + throw new Exception($"BiffColor of {biffDataType.FullName}.{member.Name} ({attr.Name}) must be either Vertex2D or Vertex3D, but is {memberType.Name}."); + } + }); + } + + private static void GetAttributes(Type attributeType, Action assert) + { + var biffDataTypes = AppDomain.CurrentDomain + .GetAssemblies() + .First(a => a.GetName().Name == "VisualPinball.Engine") + .GetTypes() + .Where(t => t.IsSubclassOf(typeof(BiffData))) + .ToArray(); + + foreach (var biffDataType in biffDataTypes) { + var members = biffDataType.GetMembers() + .Where(member => member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property); + + foreach (var member in members) { + var attrs = Attribute + .GetCustomAttributes(member, attributeType) + .Select(a => a as BiffAttribute) + .Where(a => a != null); + + foreach (var attr in attrs) { + Type memberType = null; + switch (member) { + case FieldInfo field: + memberType = field.FieldType; + break; + case PropertyInfo property: + memberType = property.PropertyType; + break; + } + + if (memberType == null) { + throw new Exception("Member type is null, that shouldn't happen because we filter by fields and properties."); + } + + assert(memberType, member, biffDataType, attr); + } + } + } + } + } +} diff --git a/VisualPinball.Engine.Test/VPT/Table/TableDataTests.cs b/VisualPinball.Engine.Test/VPT/Table/TableDataTests.cs index f398e3717..eee5046cc 100644 --- a/VisualPinball.Engine.Test/VPT/Table/TableDataTests.cs +++ b/VisualPinball.Engine.Test/VPT/Table/TableDataTests.cs @@ -12,11 +12,8 @@ public void ShouldLoadCorrectData() var table = Engine.VPT.Table.Table.Load(@"..\..\Fixtures\VPX\TableData.vpx"); var data = table.Data; - Assert.Equal(0.01435f, data._3DmaxSeparation); - Assert.Equal(0.002f, data._3DOffset); - Assert.Equal(0.53f, data._3DZPD); Assert.Equal(0.60606f, data.AngleTiltMax); - Assert.Equal(0.2033f, data.AngletiltMin); + Assert.Equal(0.2033f, data.AngleTiltMin); Assert.Equal(1.23f, data.AoScale); Assert.Equal(true, data.BallDecalMode); Assert.Equal("test_pattern", data.BallImage); @@ -48,15 +45,15 @@ public void ShouldLoadCorrectData() Assert.Equal(1.3211f, data.BgScaleZ[BackglassIndex.Desktop]); Assert.Equal(1.41f, data.BgScaleZ[BackglassIndex.Fullscreen]); Assert.Equal(12f, data.BgScaleZ[BackglassIndex.FullSingleScreen]); - Assert.Equal(12.33f, data.BgXlateX[BackglassIndex.Desktop]); - Assert.Equal(0.1f, data.BgXlateX[BackglassIndex.Fullscreen]); - Assert.Equal(0.2f, data.BgXlateX[BackglassIndex.FullSingleScreen]); - Assert.Equal(100.43f, data.BgXlateY[BackglassIndex.Desktop]); - Assert.Equal(90.1f, data.BgXlateY[BackglassIndex.Fullscreen]); - Assert.Equal(0.2332f, data.BgXlateY[BackglassIndex.FullSingleScreen]); - Assert.Equal(-50.092f, data.BgXlateZ[BackglassIndex.Desktop]); - Assert.Equal(400.1f, data.BgXlateZ[BackglassIndex.Fullscreen]); - Assert.Equal(-50.223f, data.BgXlateZ[BackglassIndex.FullSingleScreen]); + Assert.Equal(12.33f, data.BgOffsetX[BackglassIndex.Desktop]); + Assert.Equal(0.1f, data.BgOffsetX[BackglassIndex.Fullscreen]); + Assert.Equal(0.2f, data.BgOffsetX[BackglassIndex.FullSingleScreen]); + Assert.Equal(100.43f, data.BgOffsetY[BackglassIndex.Desktop]); + Assert.Equal(90.1f, data.BgOffsetY[BackglassIndex.Fullscreen]); + Assert.Equal(0.2332f, data.BgOffsetY[BackglassIndex.FullSingleScreen]); + Assert.Equal(-50.092f, data.BgOffsetZ[BackglassIndex.Desktop]); + Assert.Equal(400.1f, data.BgOffsetZ[BackglassIndex.Fullscreen]); + Assert.Equal(-50.223f, data.BgOffsetZ[BackglassIndex.FullSingleScreen]); Assert.Equal(1.5055f, data.BloomStrength); Assert.Equal(2224, data.Bottom); Assert.Equal("Option Explicit\r\n", data.Code); @@ -119,12 +116,16 @@ public void ShouldLoadCorrectData() Assert.Equal("", data.ScreenShot); Assert.Equal(true, data.ShowGrid); Assert.Equal(0.4123f, data.SsrScale); + Assert.Equal(0.01435f, data.StereoMaxSeparation); + Assert.Equal(0.002f, data.StereoOffset); + Assert.Equal(0.53f, data.StereoZeroParallaxDisplacement); Assert.Equal(6649, data.TableAdaptiveVSync); Assert.Equal(2.009231f, data.TableHeight); Assert.Equal(0.24f, data.TableMusicVolume); Assert.Equal(0.23f, data.TableSoundVolume); Assert.Equal(0f, data.Top); // set in debug mode Assert.Equal(0, data.UseAA); + Assert.Equal(0, data.UseAO); Assert.Equal(1, data.UseFXAA); Assert.Equal(7, data.UserDetailLevel); Assert.Equal(0, data.UseReflectionForBalls); diff --git a/VisualPinball.Engine.Test/VisualPinball.Engine.Test.csproj b/VisualPinball.Engine.Test/VisualPinball.Engine.Test.csproj index d17939a1b..d254155a4 100644 --- a/VisualPinball.Engine.Test/VisualPinball.Engine.Test.csproj +++ b/VisualPinball.Engine.Test/VisualPinball.Engine.Test.csproj @@ -51,6 +51,7 @@ + diff --git a/VisualPinball.Engine/IO/BiffAttribute.cs b/VisualPinball.Engine/IO/BiffAttribute.cs index 6870509a8..c74f3840a 100644 --- a/VisualPinball.Engine/IO/BiffAttribute.cs +++ b/VisualPinball.Engine/IO/BiffAttribute.cs @@ -1,9 +1,6 @@ using System; using System.IO; -using System.Linq; using System.Reflection; -using System.Text; -using VisualPinball.Engine.Math; using VisualPinball.Engine.VPT; namespace VisualPinball.Engine.IO @@ -21,7 +18,7 @@ namespace VisualPinball.Engine.IO /// COM Structured Storage /// BIFF Format [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - public class BiffAttribute : Attribute + public abstract class BiffAttribute : Attribute { /// /// Name of the BIFF record, usually four characters @@ -34,29 +31,16 @@ public class BiffAttribute : Attribute /// public bool IsStreaming; - /// - /// Wide strings have a zero byte between each character. - /// - public bool IsWideString; - - public int QuantizedUnsignedBits = -1; - public bool AsPercent = false; - /// /// For arrays, this defines how many values should be read /// - public int Count = 1; + public int Count = -1; /// /// For arrays, this defines that only one value should be read /// and stored at the given position. /// - public int Index; - - /// - /// For colors, this defines how the integer is encoded. - /// - public ColorFormat ColorFormat = ColorFormat.Bgr; + public int Index = -1; /// /// If put on a field, this is the info from C#'s reflection API. @@ -67,9 +51,9 @@ public class BiffAttribute : Attribute /// public PropertyInfo Property { get; set; } - private Type Type => Field != null ? Field.FieldType : Property.PropertyType; + protected Type Type => Field != null ? Field.FieldType : Property.PropertyType; - public BiffAttribute(string name) + protected BiffAttribute(string name) { Name = name; } @@ -90,105 +74,7 @@ public BiffAttribute(string name) /// Binary data from the VPX file /// Length of the BIFF record /// Type of the item data we're currently parsing - public virtual void Parse(T obj, BinaryReader reader, int len) where T : ItemData - { - if (Type == typeof(float)) { - SetValue(obj, ReadFloat(reader)); - - } else if (Type == typeof(int)) { - SetValue(obj, reader.ReadInt32()); - - } else if (Type == typeof(bool)) { - SetValue(obj, reader.ReadInt32() > 0); - - } else if (Type == typeof(float[])) { - if (GetValue(obj) is float[] arr) { - arr[Index] = ReadFloat(reader); - } else { - Console.Error.WriteLine($"[BiffAttribute.Parse] Expected float[] for {Name}, but got {GetValue(obj).GetType()}."); - } - - } else if (Type == typeof(uint[])) { - if (!(GetValue(obj) is uint[] arr)) { - Console.Error.WriteLine($"[BiffAttribute.Parse] Expected uint[] for {Name}, but got {GetValue(obj).GetType()}."); - return; - } - if (Count > 1) { - for (var i = 0; i < Count; i++) { - arr[i] = reader.ReadUInt32(); - } - } else { - arr[Index] = reader.ReadUInt32(); - } - - } else if (Type == typeof(string[])) { - if (GetValue(obj) is string[] arr) { - arr[Index] = ReadString(reader, len); - } else { - Console.Error.WriteLine($"[BiffAttribute.Parse] Expected string[] for {Name}, but got {GetValue(obj).GetType()}."); - } - - } else if (Type == typeof(string)) { - SetValue(obj, ReadString(reader, len)); - - } else if (Type == typeof(Vertex3D)) { - SetValue(obj, new Vertex3D(reader)); - - } else if (Type == typeof(Vertex2D)) { - SetValue(obj, new Vertex2D(reader)); - - } else if (Type == typeof(Color)) { - SetValue(obj, new Color(reader.ReadInt32(), ColorFormat)); - - } else if (Type == typeof(Color[])) { - if (GetValue(obj) is Color[] arr) { - if (Count > 1) { - for (var i = 0; i < Count; i++) { - arr[i] = new Color(reader.ReadInt32(), ColorFormat); - } - } else { - arr[Index] = new Color(reader.ReadInt32(), ColorFormat); - } - } else { - Console.Error.WriteLine($"[BiffAttribute.Parse] Expected Color[] for {Name}, but got {GetValue(obj).GetType()}."); - } - - } else { - Console.Error.WriteLine("[BiffAttribute.Parse] Unknown type \"{0}\" for tag {1}", Type, Name); - reader.BaseStream.Seek(len, SeekOrigin.Current); - } - } - - private string ReadString(BinaryReader reader, int len) - { - byte[] bytes; - if (IsWideString) { - var wideLen = reader.ReadInt32(); - bytes = reader.ReadBytes(wideLen).Where((x, i) => i % 2 == 0).ToArray(); - } else { - bytes = IsStreaming ? reader.ReadBytes(len) : reader.ReadBytes(len).Skip(4).ToArray(); - } - return Encoding.ASCII.GetString(bytes); - } - - private float ReadFloat(BinaryReader reader) - { - var f = QuantizedUnsignedBits > 0 - ? DequantizeUnsigned(QuantizedUnsignedBits, reader.ReadInt32()) - : reader.ReadSingle(); - - if (AsPercent) { - return f * 100f; - } - - return f; - } - - private float DequantizeUnsigned(int bits, int i) - { - var N = (1 << bits) - 1; - return System.Math.Min(i / (float) N, 1.0f); - } + public abstract void Parse(T obj, BinaryReader reader, int len) where T : ItemData; /// /// Sets the value to either field or property, depending on which diff --git a/VisualPinball.Engine/IO/BiffBoolAttribute.cs b/VisualPinball.Engine/IO/BiffBoolAttribute.cs new file mode 100644 index 000000000..150ea4649 --- /dev/null +++ b/VisualPinball.Engine/IO/BiffBoolAttribute.cs @@ -0,0 +1,27 @@ +using System; +using System.IO; + +namespace VisualPinball.Engine.IO +{ + public class BiffBoolAttribute : BiffAttribute + { + public BiffBoolAttribute(string name) : base(name) { } + + public override void Parse(T obj, BinaryReader reader, int len) + { + if (Type == typeof(bool)) { + SetValue(obj, reader.ReadInt32() > 0); + + } else if (Type == typeof(bool[])) { + var arr = GetValue(obj) as bool[]; + if (Count > 1) { + for (var i = 0; i < Count; i++) { + arr[i] = reader.ReadInt32() > 0; + } + } else { + arr[Index] = reader.ReadInt32() > 0; + } + } + } + } +} diff --git a/VisualPinball.Engine/IO/BiffColorAttribute.cs b/VisualPinball.Engine/IO/BiffColorAttribute.cs new file mode 100644 index 000000000..297f4b4cb --- /dev/null +++ b/VisualPinball.Engine/IO/BiffColorAttribute.cs @@ -0,0 +1,32 @@ +using System.IO; +using VisualPinball.Engine.Math; + +namespace VisualPinball.Engine.IO +{ + public class BiffColorAttribute : BiffAttribute + { + /// + /// For colors, this defines how the integer is encoded. + /// + public ColorFormat ColorFormat = ColorFormat.Bgr; + + public BiffColorAttribute(string name) : base(name) { } + + public override void Parse(T obj, BinaryReader reader, int len) + { + if (Type == typeof(Color)) { + SetValue(obj, new Color(reader.ReadInt32(), ColorFormat)); + + } else if (Type == typeof(Color[])) { + var arr = GetValue(obj) as Color[]; + if (Count > 1) { + for (var i = 0; i < Count; i++) { + arr[i] = new Color(reader.ReadInt32(), ColorFormat); + } + } else { + arr[Index] = new Color(reader.ReadInt32(), ColorFormat); + } + } + } + } +} diff --git a/VisualPinball.Engine/IO/BiffFloatAttribute.cs b/VisualPinball.Engine/IO/BiffFloatAttribute.cs new file mode 100644 index 000000000..5a7771c30 --- /dev/null +++ b/VisualPinball.Engine/IO/BiffFloatAttribute.cs @@ -0,0 +1,49 @@ +using System; +using System.IO; + +namespace VisualPinball.Engine.IO +{ + public class BiffFloatAttribute : BiffAttribute + { + public int QuantizedUnsignedBits = -1; + public bool AsPercent = false; + + public BiffFloatAttribute(string name) : base(name) { } + + public override void Parse(T obj, BinaryReader reader, int len) + { + if (Type == typeof(float)) { + SetValue(obj, ReadFloat(reader)); + + } else if (Type == typeof(float[])) { + var arr = GetValue(obj) as float[]; + if (Count > 1) { + for (var i = 0; i < Count; i++) { + arr[i] = ReadFloat(reader); + } + } else { + arr[Index] = ReadFloat(reader); + } + } + } + + private float ReadFloat(BinaryReader reader) + { + var f = QuantizedUnsignedBits > 0 + ? DequantizeUnsigned(QuantizedUnsignedBits, reader.ReadInt32()) + : reader.ReadSingle(); + + if (AsPercent) { + return f * 100f; + } + + return f; + } + + public static float DequantizeUnsigned(int bits, int i) + { + var N = (1 << bits) - 1; + return System.Math.Min(i / (float) N, 1.0f); + } + } +} diff --git a/VisualPinball.Engine/IO/BiffIntAttribute.cs b/VisualPinball.Engine/IO/BiffIntAttribute.cs new file mode 100644 index 000000000..ab8f576b5 --- /dev/null +++ b/VisualPinball.Engine/IO/BiffIntAttribute.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; + +namespace VisualPinball.Engine.IO +{ + public class BiffIntAttribute : BiffAttribute + { + public BiffIntAttribute(string name) : base(name) { } + + public override void Parse(T obj, BinaryReader reader, int len) + { + if (Type == typeof(int)) { + SetValue(obj, reader.ReadInt32()); + + } else if (Type == typeof(int[])) { + var arr = GetValue(obj) as int[]; + if (Count > 1) { + for (var i = 0; i < Count; i++) { + arr[i] = reader.ReadInt32(); + } + } else { + arr[Index] = reader.ReadInt32(); + } + + } else if (Type == typeof(uint)) { + SetValue(obj, reader.ReadUInt32()); + + } else if (Type == typeof(uint[])) { + var arr = GetValue(obj) as uint[]; + if (Count > 1) { + for (var i = 0; i < Count; i++) { + arr[i] = reader.ReadUInt32(); + } + } else { + arr[Index] = reader.ReadUInt32(); + } + } + } + } +} diff --git a/VisualPinball.Engine/IO/BiffStringAttribute.cs b/VisualPinball.Engine/IO/BiffStringAttribute.cs new file mode 100644 index 000000000..f24d9fc20 --- /dev/null +++ b/VisualPinball.Engine/IO/BiffStringAttribute.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; + +namespace VisualPinball.Engine.IO +{ + public class BiffStringAttribute : BiffAttribute + { + /// + /// Wide strings have a zero byte between each character. + /// + public bool IsWideString; + + public BiffStringAttribute(string name) : base(name) { } + + public override void Parse(T obj, BinaryReader reader, int len) + { + if (Type == typeof(string)) { + SetValue(obj, ReadString(reader, len)); + + } else if (Type == typeof(string[])) { + var arr = GetValue(obj) as string[]; + if (Count > 1) { + for (var i = 0; i < Count; i++) { + arr[i] = ReadString(reader, len); + } + } else { + arr[Index] = ReadString(reader, len); + } + } + } + + private string ReadString(BinaryReader reader, int len) + { + byte[] bytes; + if (IsWideString) { + var wideLen = reader.ReadInt32(); + bytes = reader.ReadBytes(wideLen).Where((x, i) => i % 2 == 0).ToArray(); + } else { + bytes = IsStreaming ? reader.ReadBytes(len) : reader.ReadBytes(len).Skip(4).ToArray(); + } + return Encoding.ASCII.GetString(bytes); + } + } +} diff --git a/VisualPinball.Engine/IO/BiffVertexAttribute.cs b/VisualPinball.Engine/IO/BiffVertexAttribute.cs new file mode 100644 index 000000000..21ced96eb --- /dev/null +++ b/VisualPinball.Engine/IO/BiffVertexAttribute.cs @@ -0,0 +1,20 @@ +using System.IO; +using VisualPinball.Engine.Math; + +namespace VisualPinball.Engine.IO +{ + public class BiffVertexAttribute : BiffAttribute + { + public BiffVertexAttribute(string name) : base(name) { } + + public override void Parse(T obj, BinaryReader reader, int len) + { + if (Type == typeof(Vertex3D)) { + SetValue(obj, new Vertex3D(reader)); + + } else if (Type == typeof(Vertex2D)) { + SetValue(obj, new Vertex2D(reader)); + } + } + } +} diff --git a/VisualPinball.Engine/VPT/ItemData.cs b/VisualPinball.Engine/VPT/ItemData.cs index e1b63332d..baf030d7a 100644 --- a/VisualPinball.Engine/VPT/ItemData.cs +++ b/VisualPinball.Engine/VPT/ItemData.cs @@ -12,7 +12,7 @@ namespace VisualPinball.Engine.VPT /// public class ItemData : BiffData { - [Biff("NAME", IsWideString = true)] + [BiffString("NAME", IsWideString = true)] public string Name; public readonly string StorageName; diff --git a/VisualPinball.Engine/VPT/Material.cs b/VisualPinball.Engine/VPT/Material.cs index 8843ffb7e..dff553901 100644 --- a/VisualPinball.Engine/VPT/Material.cs +++ b/VisualPinball.Engine/VPT/Material.cs @@ -95,13 +95,13 @@ public Material(BinaryReader reader) ClearCoat = BiffUtil.BgrToRgb(saveMaterial.ClearCoat); WrapLighting = saveMaterial.WrapLighting; Roughness = saveMaterial.Roughness; - GlossyImageLerp = 0; //1.0f - dequantizeUnsigned<8>(mats[i].fGlossyImageLerp); //!! '1.0f -' to be compatible with previous table versions - Thickness = 0; //(mats[i].fThickness == 0) ? 0.05f : dequantizeUnsigned<8>(mats[i].fThickness); //!! 0 -> 0.05f to be compatible with previous table versions + GlossyImageLerp = 1f - BiffFloatAttribute.DequantizeUnsigned(8, saveMaterial.GlossyImageLerp); //1.0f - dequantizeUnsigned<8>(mats[i].fGlossyImageLerp); //!! '1.0f -' to be compatible with previous table versions + Thickness = saveMaterial.Thickness == 0 ? 0.05f : BiffFloatAttribute.DequantizeUnsigned(8, saveMaterial.Thickness); //!! 0 -> 0.05f to be compatible with previous table versions Edge = saveMaterial.Edge; Opacity = saveMaterial.Opacity; IsMetal = saveMaterial.IsMetal; IsOpacityActive = (saveMaterial.OpacityActiveEdgeAlpha & 1) != 0; - EdgeAlpha = 0; //dequantizeUnsigned<7>(mats[i].bOpacityActiveEdgeAlpha >> 1); + EdgeAlpha = BiffFloatAttribute.DequantizeUnsigned(7, saveMaterial.OpacityActiveEdgeAlpha); //dequantizeUnsigned<7>(mats[i].bOpacityActiveEdgeAlpha >> 1); } public void UpdatePhysics(SavePhysicsMaterial savePhysMat) { diff --git a/VisualPinball.Engine/VPT/Primitive/PrimitiveData.cs b/VisualPinball.Engine/VPT/Primitive/PrimitiveData.cs index 3a406e444..3eba1520f 100644 --- a/VisualPinball.Engine/VPT/Primitive/PrimitiveData.cs +++ b/VisualPinball.Engine/VPT/Primitive/PrimitiveData.cs @@ -1,3 +1,9 @@ +#region ReSharper +// ReSharper disable UnassignedField.Global +// ReSharper disable StringLiteralTypo +// ReSharper disable FieldCanBeMadeReadOnly.Global +#endregion + using System; using System.Collections.Generic; using System.IO; @@ -8,10 +14,10 @@ namespace VisualPinball.Engine.VPT.Primitive { public class PrimitiveData : ItemData { - [Biff("VPOS")] + [BiffVertex("VPOS")] public Vertex3D Position; - [Biff("VSIZ")] + [BiffVertex("VSIZ")] public Vertex3D Size = new Vertex3D(100, 100, 100); [BiffVertices("M3DX")] @@ -20,111 +26,111 @@ public class PrimitiveData : ItemData [BiffIndices("M3CI", IsCompressed = true)] public readonly Mesh Mesh = new Mesh(); - [Biff("RTV0", Index = 0)] - [Biff("RTV1", Index = 1)] - [Biff("RTV2", Index = 2)] - [Biff("RTV3", Index = 3)] - [Biff("RTV4", Index = 4)] - [Biff("RTV5", Index = 5)] - [Biff("RTV6", Index = 6)] - [Biff("RTV7", Index = 7)] - [Biff("RTV8", Index = 8)] + [BiffFloat("RTV0", Index = 0)] + [BiffFloat("RTV1", Index = 1)] + [BiffFloat("RTV2", Index = 2)] + [BiffFloat("RTV3", Index = 3)] + [BiffFloat("RTV4", Index = 4)] + [BiffFloat("RTV5", Index = 5)] + [BiffFloat("RTV6", Index = 6)] + [BiffFloat("RTV7", Index = 7)] + [BiffFloat("RTV8", Index = 8)] public float[] RotAndTra = new float[9]; - [Biff("IMAG")] + [BiffString("IMAG")] public string Image; - [Biff("NRMA")] + [BiffString("NRMA")] public string NormalMap; - [Biff("SIDS")] + [BiffInt("SIDS")] public int Sides; - [Biff("MATR")] + [BiffString("MATR")] public string Material; - [Biff("SCOL")] + [BiffInt("SCOL")] public int SideColor; - [Biff("TVIS")] + [BiffBool("TVIS")] public bool IsVisible; - [Biff("REEN")] + [BiffBool("REEN")] public bool IsReflectionEnabled; - [Biff("DTXI")] + [BiffBool("DTXI")] public bool DrawTexturesInside; - [Biff("HTEV")] + [BiffBool("HTEV")] public bool HitEvent; - [Biff("THRS")] + [BiffFloat("THRS")] public float Threshold; - [Biff("ELAS")] + [BiffFloat("ELAS")] public float Elasticity; - [Biff("ELFO")] + [BiffFloat("ELFO")] public float ElasticityFalloff; - [Biff("RFCT")] + [BiffFloat("RFCT")] public float Friction; - [Biff("RSCT")] + [BiffFloat("RSCT")] public float Scatter; - [Biff("EFUI")] + [BiffFloat("EFUI")] public float EdgeFactorUi; - [Biff("CORF")] + [BiffFloat("CORF")] public float CollisionReductionFactor = 0; - [Biff("CLDR")] + [BiffBool("CLDR")] public bool IsCollidable = true; // originally "CLDRP" - [Biff("ISTO")] + [BiffBool("ISTO")] public bool IsToy; - [Biff("MAPH")] + [BiffString("MAPH")] public string PhysicsMaterial; - [Biff("OVPH")] + [BiffBool("OVPH")] public bool OverwritePhysics; - [Biff("STRE")] + [BiffBool("STRE")] public bool StaticRendering; - [Biff("DILI")] + [BiffFloat("DILI")] public float DisableLightingTop; // m_d.m_fDisableLightingTop = (tmp == 1) ? 1.f : dequantizeUnsigned<8>(tmp); // backwards compatible hacky loading! - [Biff("DILB")] + [BiffFloat("DILB")] public float DisableLightingBelow; - [Biff("U3DM")] + [BiffBool("U3DM")] public bool Use3DMesh; - [Biff("EBFC")] + [BiffBool("EBFC")] public bool BackfacesEnabled; - [Biff("DIPT")] + [BiffBool("DIPT")] public bool DisplayTexture; - [Biff("M3DN", IsWideString = true)] + [BiffString("M3DN", IsWideString = true)] public string MeshFileName; - [Biff("M3VN")] - public int NumVertices = 0; + [BiffInt("M3VN")] + public int NumVertices; - [Biff("M3CY")] - public int CompressedVertices = 0; + [BiffInt("M3CY")] + public int CompressedVertices; - [Biff("M3FN")] - public int NumIndices = 0; + [BiffInt("M3FN")] + public int NumIndices; - [Biff("M3CJ")] + [BiffInt("M3CJ")] public int CompressedIndices = 0; - [Biff("PIDB")] + [BiffFloat("PIDB")] public float DepthBias = 0; static PrimitiveData() @@ -167,8 +173,6 @@ public override void Parse(T obj, BinaryReader reader, int len) throw new Exception($"Error parsing vertices for {obj.Name} ({obj.StorageName}).", e); } - } else { - base.Parse(obj, reader, len); } } @@ -216,9 +220,6 @@ public override void Parse(T obj, BinaryReader reader, int len) ParseIndices(tableData, IsCompressed ? BiffZlib.Decompress(reader.ReadBytes(len)) : reader.ReadBytes(len)); - - } else { - base.Parse(obj, reader, len); } } @@ -242,5 +243,4 @@ private void ParseIndices(PrimitiveData data, byte[] bytes) mesh.Indices = indices; } } - } diff --git a/VisualPinball.Engine/VPT/Table/TableData.cs b/VisualPinball.Engine/VPT/Table/TableData.cs index 3216a03dc..0ba65c0ca 100644 --- a/VisualPinball.Engine/VPT/Table/TableData.cs +++ b/VisualPinball.Engine/VPT/Table/TableData.cs @@ -1,7 +1,7 @@ #region ReSharper // ReSharper disable UnassignedField.Global // ReSharper disable StringLiteralTypo -// ReSharper disable IdentifierTypo +// ReSharper disable FieldCanBeMadeReadOnly.Global #endregion using System; @@ -11,297 +11,298 @@ using VisualPinball.Engine.IO; using VisualPinball.Engine.Math; + namespace VisualPinball.Engine.VPT.Table { public class TableData : ItemData { - [Biff("LEFT")] + [BiffFloat("LEFT")] public float Left; - [Biff("RGHT")] + [BiffFloat("RGHT")] public float Right; - [Biff("TOPX")] + [BiffFloat("TOPX")] public float Top; - [Biff("BOTM")] + [BiffFloat("BOTM")] public float Bottom; - [Biff("EFSS")] + [BiffBool("EFSS")] public bool BgEnableFss; - [Biff("ORRP")] + [BiffBool("ORRP")] public bool OverridePhysics; - [Biff("ORPF")] + [BiffBool("ORPF")] public bool OverridePhysicsFlipper; - [Biff("GAVT")] + [BiffFloat("GAVT")] public float Gravity; - [Biff("FRCT")] + [BiffFloat("FRCT")] public float Friction; - [Biff("ELAS")] + [BiffFloat("ELAS")] public float Elasticity; - [Biff("ELFA")] + [BiffFloat("ELFA")] public float ElasticityFalloff; - [Biff("PFSC")] + [BiffFloat("PFSC")] public float Scatter; - [Biff("SCAT")] + [BiffFloat("SCAT")] public float DefaultScatter; - [Biff("NDGT")] + [BiffFloat("NDGT")] public float NudgeTime; - [Biff("MPGC")] + [BiffInt("MPGC")] public int PlungerNormalize; - [Biff("MPDF")] + [BiffBool("MPDF")] public bool PlungerFilter; - [Biff("PHML")] + [BiffInt("PHML")] public int PhysicsMaxLoops; - [Biff("DECL")] + [BiffBool("DECL")] public bool RenderDecals; - [Biff("REEL")] + [BiffBool("REEL")] public bool RenderEmReels; - [Biff("OFFX", Index = 0)] - [Biff("OFFY", Index = 1)] + [BiffFloat("OFFX", Index = 0)] + [BiffFloat("OFFY", Index = 1)] public float[] Offset = new float[2]; - [Biff("ZOOM")] + [BiffFloat("ZOOM")] public float Zoom; - [Biff("MAXS")] - public float _3DmaxSeparation; + [BiffFloat("MAXS")] + public float StereoMaxSeparation; - [Biff("ZPD")] - public float _3DZPD; + [BiffFloat("ZPD")] + public float StereoZeroParallaxDisplacement; - [Biff("STO")] - public float _3DOffset; + [BiffFloat("STO")] + public float StereoOffset; - [Biff("OGST")] + [BiffBool("OGST")] public bool OverwriteGlobalStereo3D; - [Biff("SLPX")] + [BiffFloat("SLPX")] public float AngleTiltMax; - [Biff("SLOP")] - public float AngletiltMin; + [BiffFloat("SLOP")] + public float AngleTiltMin; - [Biff("GLAS")] + [BiffFloat("GLAS")] public float GlassHeight; - [Biff("TBLH")] + [BiffFloat("TBLH")] public float TableHeight; - [Biff("IMAG")] + [BiffString("IMAG")] public string Image; - [Biff("BLIM")] + [BiffString("BLIM")] public string BallImage; - [Biff("BLIF")] + [BiffString("BLIF")] public string BallImageFront; - [Biff("SSHT")] + [BiffString("SSHT")] public string ScreenShot; - [Biff("FBCK")] + [BiffBool("FBCK")] public bool DisplayBackdrop; - [Biff("SEDT")] + [BiffInt("SEDT")] public int NumGameItems; - [Biff("SSND")] + [BiffInt("SSND")] public int NumSounds; - [Biff("SIMG")] + [BiffInt("SIMG")] public int NumTextures; - [Biff("SFNT")] + [BiffInt("SFNT")] public int NumFonts; - [Biff("SCOL")] + [BiffInt("SCOL")] public int NumCollections; - [Biff("BIMN")] + [BiffBool("BIMN")] public bool ImageBackdropNightDay; - [Biff("IMCG")] + [BiffString("IMCG")] public string ImageColorGrade; - [Biff("EIMG")] + [BiffString("EIMG")] public string EnvImage; - [Biff("PLMA")] + [BiffString("PLMA")] public string PlayfieldMaterial; - [Biff("LZAM")] + [BiffColor("LZAM")] public Color LightAmbient; - [Biff("LZDI")] + [BiffInt("LZDI")] public int Light0Emission { set => Light[0].Emission = new Color(value, ColorFormat.Bgr); } public readonly LightSource[] Light = { new LightSource() }; - [Biff("LZHI")] + [BiffFloat("LZHI")] public float LightHeight; - [Biff("LZRA")] + [BiffFloat("LZRA")] public float LightRange; - [Biff("LIES")] + [BiffFloat("LIES")] public float LightEmissionScale; - [Biff("ENES")] + [BiffFloat("ENES")] public float EnvEmissionScale; - [Biff("GLES")] + [BiffFloat("GLES")] public float GlobalEmissionScale; - [Biff("AOSC")] + [BiffFloat("AOSC")] public float AoScale; - [Biff("SSSC")] + [BiffFloat("SSSC")] public float SsrScale; - [Biff("BREF")] + [BiffInt("BREF")] public int UseReflectionForBalls; - [Biff("PLST", QuantizedUnsignedBits = 8)] + [BiffFloat("PLST", QuantizedUnsignedBits = 8)] public float PlayfieldReflectionStrength; - [Biff("BTRA")] + [BiffInt("BTRA")] public int UseTrailForBalls; - [Biff("BTST", QuantizedUnsignedBits = 8)] + [BiffFloat("BTST", QuantizedUnsignedBits = 8)] public float BallTrailStrength; - [Biff("BPRS")] + [BiffFloat("BPRS")] public float BallPlayfieldReflectionStrength; - [Biff("DBIS")] + [BiffFloat("DBIS")] public float DefaultBulbIntensityScaleOnBall; - [Biff("UAAL")] + [BiffInt("UAAL")] public int UseAA; - [Biff("UAOC")] + [BiffInt("UAOC")] public int UseAO; - [Biff("USSR")] + [BiffInt("USSR")] public int UseSSR; - [Biff("UFXA")] + [BiffInt("UFXA")] public int UseFXAA; - [Biff("BLST")] + [BiffFloat("BLST")] public float BloomStrength; - [Biff("BCLR", ColorFormat = ColorFormat.Bgr)] + [BiffColor("BCLR", ColorFormat = ColorFormat.Bgr)] public Color ColorBackdrop; - [Biff("CCUS", Count = 16)] + [BiffColor("CCUS", Count = 16)] public Color[] CustomColors = new Color[16]; - [Biff("TDFT")] + [BiffFloat("TDFT")] public float GlobalDifficulty; - [Biff("SVOL")] + [BiffFloat("SVOL")] public float TableSoundVolume; - [Biff("BDMO")] + [BiffBool("BDMO")] public bool BallDecalMode; - [Biff("MVOL")] + [BiffFloat("MVOL")] public float TableMusicVolume; - [Biff("AVSY")] + [BiffInt("AVSY")] public int TableAdaptiveVSync; - [Biff("OGAC")] + [BiffBool("OGAC")] public bool OverwriteGlobalDetailLevel; - [Biff("OGDN")] + [BiffBool("OGDN")] public bool OverwriteGlobalDayNight; - [Biff("GDAC")] + [BiffBool("GDAC")] public bool ShowGrid; - [Biff("REOP")] + [BiffBool("REOP")] public bool ReflectElementsOnPlayfield; - [Biff("ARAC")] + [BiffInt("ARAC")] public int UserDetailLevel; - [Biff("MASI")] + [BiffInt("MASI")] public int NumMaterials; - [Biff("CODE", IsStreaming = true)] + [BiffString("CODE", IsStreaming = true)] public string Code; - [Biff("ROTA", Index = BackglassIndex.Desktop)] - [Biff("ROTF", Index = BackglassIndex.Fullscreen)] - [Biff("ROFS", Index = BackglassIndex.FullSingleScreen)] + [BiffFloat("ROTA", Index = BackglassIndex.Desktop)] + [BiffFloat("ROTF", Index = BackglassIndex.Fullscreen)] + [BiffFloat("ROFS", Index = BackglassIndex.FullSingleScreen)] public readonly float[] BgRotation = new float[3]; - [Biff("LAYB", Index = BackglassIndex.Desktop)] - [Biff("LAYF", Index = BackglassIndex.Fullscreen)] - [Biff("LAFS", Index = BackglassIndex.FullSingleScreen)] + [BiffFloat("LAYB", Index = BackglassIndex.Desktop)] + [BiffFloat("LAYF", Index = BackglassIndex.Fullscreen)] + [BiffFloat("LAFS", Index = BackglassIndex.FullSingleScreen)] public readonly float[] BgLayback = new float[3]; - [Biff("INCL", Index = BackglassIndex.Desktop)] - [Biff("INCF", Index = BackglassIndex.Fullscreen)] - [Biff("INFS", Index = BackglassIndex.FullSingleScreen)] + [BiffFloat("INCL", Index = BackglassIndex.Desktop)] + [BiffFloat("INCF", Index = BackglassIndex.Fullscreen)] + [BiffFloat("INFS", Index = BackglassIndex.FullSingleScreen)] public readonly float[] BgInclination = new float[3]; - [Biff("FOVX", Index = BackglassIndex.Desktop)] - [Biff("FOVF", Index = BackglassIndex.Fullscreen)] - [Biff("FOFS", Index = BackglassIndex.FullSingleScreen)] + [BiffFloat("FOVX", Index = BackglassIndex.Desktop)] + [BiffFloat("FOVF", Index = BackglassIndex.Fullscreen)] + [BiffFloat("FOFS", Index = BackglassIndex.FullSingleScreen)] public readonly float[] BgFov = new float[3]; - [Biff("SCLX", Index = BackglassIndex.Desktop)] - [Biff("SCFX", Index = BackglassIndex.Fullscreen)] - [Biff("SCXS", Index = BackglassIndex.FullSingleScreen)] + [BiffFloat("SCLX", Index = BackglassIndex.Desktop)] + [BiffFloat("SCFX", Index = BackglassIndex.Fullscreen)] + [BiffFloat("SCXS", Index = BackglassIndex.FullSingleScreen)] public readonly float[] BgScaleX = new float[3]; - [Biff("SCLY", Index = BackglassIndex.Desktop)] - [Biff("SCFY", Index = BackglassIndex.Fullscreen)] - [Biff("SCYS", Index = BackglassIndex.FullSingleScreen)] + [BiffFloat("SCLY", Index = BackglassIndex.Desktop)] + [BiffFloat("SCFY", Index = BackglassIndex.Fullscreen)] + [BiffFloat("SCYS", Index = BackglassIndex.FullSingleScreen)] public readonly float[] BgScaleY = new float[3]; - [Biff("SCLZ", Index = BackglassIndex.Desktop)] - [Biff("SCFZ", Index = BackglassIndex.Fullscreen)] - [Biff("SCZS", Index = BackglassIndex.FullSingleScreen)] + [BiffFloat("SCLZ", Index = BackglassIndex.Desktop)] + [BiffFloat("SCFZ", Index = BackglassIndex.Fullscreen)] + [BiffFloat("SCZS", Index = BackglassIndex.FullSingleScreen)] public readonly float[] BgScaleZ = new float[3]; - [Biff("XLTX", Index = BackglassIndex.Desktop)] - [Biff("XLFX", Index = BackglassIndex.Fullscreen)] - [Biff("XLXS", Index = BackglassIndex.FullSingleScreen)] - public readonly float[] BgXlateX = new float[3]; + [BiffFloat("XLTX", Index = BackglassIndex.Desktop)] + [BiffFloat("XLFX", Index = BackglassIndex.Fullscreen)] + [BiffFloat("XLXS", Index = BackglassIndex.FullSingleScreen)] + public readonly float[] BgOffsetX = new float[3]; - [Biff("XLTY", Index = BackglassIndex.Desktop)] - [Biff("XLFY", Index = BackglassIndex.Fullscreen)] - [Biff("XLYS", Index = BackglassIndex.FullSingleScreen)] - public readonly float[] BgXlateY = new float[3]; + [BiffFloat("XLTY", Index = BackglassIndex.Desktop)] + [BiffFloat("XLFY", Index = BackglassIndex.Fullscreen)] + [BiffFloat("XLYS", Index = BackglassIndex.FullSingleScreen)] + public readonly float[] BgOffsetY = new float[3]; - [Biff("XLTZ", Index = BackglassIndex.Desktop)] - [Biff("XLFZ", Index = BackglassIndex.Fullscreen)] - [Biff("XLZS", Index = BackglassIndex.FullSingleScreen)] - public readonly float[] BgXlateZ = new float[3]; + [BiffFloat("XLTZ", Index = BackglassIndex.Desktop)] + [BiffFloat("XLFZ", Index = BackglassIndex.Fullscreen)] + [BiffFloat("XLZS", Index = BackglassIndex.FullSingleScreen)] + public readonly float[] BgOffsetZ = new float[3]; - [Biff("BIMG", Index = BackglassIndex.Desktop)] - [Biff("BIMF", Index = BackglassIndex.Fullscreen)] - [Biff("BIMS", Index = BackglassIndex.FullSingleScreen)] + [BiffString("BIMG", Index = BackglassIndex.Desktop)] + [BiffString("BIMF", Index = BackglassIndex.Fullscreen)] + [BiffString("BIMS", Index = BackglassIndex.FullSingleScreen)] public readonly string[] BgImage = new string[3]; [BiffMaterials("MATE")] @@ -345,8 +346,6 @@ public override void Parse(T obj, BinaryReader reader, int len) } else { ParseMaterial(tableData, reader, len); } - } else { - base.Parse(obj, reader, len); } } @@ -384,6 +383,6 @@ private void ParsePhysicsMaterial(TableData tableData, BinaryReader reader, int public class LightSource { public Color Emission; - public dynamic Pos; //: Vertex3D; + public Vertex3D Pos; } } diff --git a/VisualPinball.Engine/VisualPinball.Engine.csproj b/VisualPinball.Engine/VisualPinball.Engine.csproj index 3ff8d61f9..9e2b88838 100644 --- a/VisualPinball.Engine/VisualPinball.Engine.csproj +++ b/VisualPinball.Engine/VisualPinball.Engine.csproj @@ -11,6 +11,7 @@ VisualPinball.Engine v4.7.1 512 + 7.1 AnyCPU @@ -47,8 +48,14 @@ + + + + + + diff --git a/VisualPinball.Unity/VisualPinball.Unity.csproj b/VisualPinball.Unity/VisualPinball.Unity.csproj index 99df40555..b2eafe13a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.csproj +++ b/VisualPinball.Unity/VisualPinball.Unity.csproj @@ -11,6 +11,7 @@ VisualPinball.Unity v4.7.1 512 + 7.1 AnyCPU