From e756e313b03897f7f612dcfb118e358c4c5a92fc Mon Sep 17 00:00:00 2001 From: Yaakov Date: Tue, 7 Nov 2023 05:26:44 +1100 Subject: [PATCH] Add support for (de)serializing CLR boolean values. (#83) This should match the Valve implementation precisely. Fixes #81. --- .../Test Data/Text/boolean.vdf | 6 ++ .../Test Data/Text/boolean_serialization.vdf | 5 ++ .../Test Data/apisurface.txt | 1 + .../Text/BooleanTestCase.cs | 83 +++++++++++++++++++ ValveKeyValue/ValveKeyValue/KVObjectValue.cs | 2 +- .../ValveKeyValue/KVValue_operators.cs | 9 ++ ValveKeyValue/ValveKeyValue/ObjectCopier.cs | 2 +- 7 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 ValveKeyValue/ValveKeyValue.Test/Test Data/Text/boolean.vdf create mode 100644 ValveKeyValue/ValveKeyValue.Test/Test Data/Text/boolean_serialization.vdf create mode 100644 ValveKeyValue/ValveKeyValue.Test/Text/BooleanTestCase.cs diff --git a/ValveKeyValue/ValveKeyValue.Test/Test Data/Text/boolean.vdf b/ValveKeyValue/ValveKeyValue.Test/Test Data/Text/boolean.vdf new file mode 100644 index 00000000..af5d289c --- /dev/null +++ b/ValveKeyValue/ValveKeyValue.Test/Test Data/Text/boolean.vdf @@ -0,0 +1,6 @@ +"object" +{ + "test1_false" "0" + "test2_true" "1" + "test3_oob" "2" +} \ No newline at end of file diff --git a/ValveKeyValue/ValveKeyValue.Test/Test Data/Text/boolean_serialization.vdf b/ValveKeyValue/ValveKeyValue.Test/Test Data/Text/boolean_serialization.vdf new file mode 100644 index 00000000..8a6089cd --- /dev/null +++ b/ValveKeyValue/ValveKeyValue.Test/Test Data/Text/boolean_serialization.vdf @@ -0,0 +1,5 @@ +"object" +{ + "test1_false" "0" + "test2_true" "1" +} \ No newline at end of file diff --git a/ValveKeyValue/ValveKeyValue.Test/Test Data/apisurface.txt b/ValveKeyValue/ValveKeyValue.Test/Test Data/apisurface.txt index 4f6f1da1..6e004778 100644 --- a/ValveKeyValue/ValveKeyValue.Test/Test Data/apisurface.txt +++ b/ValveKeyValue/ValveKeyValue.Test/Test Data/apisurface.txt @@ -152,6 +152,7 @@ public class ValveKeyValue.KVValue public static IntPtr op_Explicit(ValveKeyValue.KVValue value); public static ValveKeyValue.KVValue op_Implicit(string value); public static ValveKeyValue.KVValue op_Implicit(int value); + public static ValveKeyValue.KVValue op_Implicit(bool value); public static ValveKeyValue.KVValue op_Implicit(IntPtr value); public static ValveKeyValue.KVValue op_Implicit(ulong value); public static ValveKeyValue.KVValue op_Implicit(float value); diff --git a/ValveKeyValue/ValveKeyValue.Test/Text/BooleanTestCase.cs b/ValveKeyValue/ValveKeyValue.Test/Text/BooleanTestCase.cs new file mode 100644 index 00000000..15b3eb95 --- /dev/null +++ b/ValveKeyValue/ValveKeyValue.Test/Text/BooleanTestCase.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace ValveKeyValue.Test +{ + class BooleanTestCase + { + [Test] + public void DynamicDeserialization() + { + using var stream = TestDataHelper.OpenResource("Text.boolean.vdf"); + var data = KVSerializer.Create(KVSerializationFormat.KeyValues1Text).Deserialize(stream); + + Assert.Multiple(() => + { + Assert.That((bool)data["test1_false"], Is.False, "test1_false"); + Assert.That((bool)data["test2_true"], Is.True, "test2_true"); + Assert.That((bool)data["test3_oob"], Is.True, "test3_oob"); + }); + } + + [Test] + public void StronglyTypedDeserialization() + { + using var stream = TestDataHelper.OpenResource("Text.boolean.vdf"); + var data = KVSerializer.Create(KVSerializationFormat.KeyValues1Text).Deserialize(stream); + + Assert.Multiple(() => + { + Assert.That(data.test1_false, Is.False, nameof(data.test1_false)); + Assert.That(data.test2_true, Is.True, nameof(data.test2_true)); + Assert.That(data.test3_oob, Is.True, nameof(data.test3_oob)); + }); + } + + [Test] + public void DynamicSerialization() + { + var data = new KVObject("object", new[] + { + new KVObject("test1_false", false), + new KVObject("test2_true", true), + }); + + var expected = TestDataHelper.ReadTextResource("Text.boolean_serialization.vdf"); + + using var ms = new MemoryStream(); + KVSerializer.Create(KVSerializationFormat.KeyValues1Text).Serialize(ms, data); + var text = Encoding.UTF8.GetString(ms.ToArray()); + + Assert.That(text, Is.EqualTo(expected)); + } + + [Test] + public void StronglyTypedSerialization() + { + var data = new + { + test1_false = false, + test2_true = true, + }; + + var expected = TestDataHelper.ReadTextResource("Text.boolean_serialization.vdf"); + + using var ms = new MemoryStream(); + KVSerializer.Create(KVSerializationFormat.KeyValues1Text).Serialize(ms, data, "object"); + var text = Encoding.UTF8.GetString(ms.ToArray()); + + Assert.That(text, Is.EqualTo(expected)); + } + + class SerializedType + { + public bool test1_false { get; set; } + public bool test2_true { get; set; } + public bool test3_oob { get; set; } + } + } +} diff --git a/ValveKeyValue/ValveKeyValue/KVObjectValue.cs b/ValveKeyValue/ValveKeyValue/KVObjectValue.cs index 19f573d5..09a1dd41 100644 --- a/ValveKeyValue/ValveKeyValue/KVObjectValue.cs +++ b/ValveKeyValue/ValveKeyValue/KVObjectValue.cs @@ -35,7 +35,7 @@ public override TypeCode GetTypeCode() }; } - public override bool ToBoolean(IFormatProvider provider) => ToInt32(provider) == 1; + public override bool ToBoolean(IFormatProvider provider) => ToInt32(provider) != 0; public override byte ToByte(IFormatProvider provider) => (byte)Convert.ChangeType(value, typeof(byte), provider); diff --git a/ValveKeyValue/ValveKeyValue/KVValue_operators.cs b/ValveKeyValue/ValveKeyValue/KVValue_operators.cs index 13d31b16..fa7f03af 100644 --- a/ValveKeyValue/ValveKeyValue/KVValue_operators.cs +++ b/ValveKeyValue/ValveKeyValue/KVValue_operators.cs @@ -26,6 +26,15 @@ public static implicit operator KVValue(int value) return new KVObjectValue(value, KVValueType.Int32); } + /// + /// Implicit cast operator for to KVValue. + /// + /// The to cast. + public static implicit operator KVValue(bool value) + { + return new KVObjectValue(value ? 1 : 0, KVValueType.Int32); + } + /// /// Implicit cast operator for to KVValue. /// diff --git a/ValveKeyValue/ValveKeyValue/ObjectCopier.cs b/ValveKeyValue/ValveKeyValue/ObjectCopier.cs index cbd24f45..e296011b 100644 --- a/ValveKeyValue/ValveKeyValue/ObjectCopier.cs +++ b/ValveKeyValue/ValveKeyValue/ObjectCopier.cs @@ -437,7 +437,7 @@ static KVValue ConvertToKVValue(object value, Type type) return Type.GetTypeCode(type) switch { - //TypeCode.Boolean => throw new NotImplementedException("Converting to boolean is not yet supported"), + TypeCode.Boolean => (KVValue)(bool)value, //TypeCode.Byte => throw new NotImplementedException("Converting to byte is not yet supported"), //TypeCode.Char => throw new NotImplementedException("Converting to char is not yet supported"), //TypeCode.DateTime => throw new NotImplementedException(), // Datetime are not supported