Skip to content

Commit a157a15

Browse files
committed
Implement binary blobs
1 parent ff6bbe7 commit a157a15

File tree

7 files changed

+208
-13
lines changed

7 files changed

+208
-13
lines changed

ValveKeyValue/ValveKeyValue.Test/Test Data/TextKV3/array_kv1.vdf

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@
3434
"2" "3"
3535
"3" "7"
3636
}
37-
"7" "hello.world"
38-
"8" "multiline
37+
"7" "11 FF"
38+
"8" "hello.world"
39+
"9" "multiline
3940
string"
40-
"9" "-69.42"
41+
"10" "-69.42"
4142
}
4243
"test" "success"
4344
}

ValveKeyValue/ValveKeyValue.Test/TextKV3/BasicKV3TestCases.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,12 @@ public void DeserializesArray()
9090
using var stream = TestDataHelper.OpenResource("TextKV3.array.kv3");
9191
var data = KVSerializer.Create(KVSerializationFormat.KeyValues3Text).Deserialize(stream);
9292

93-
Assert.True(false);
93+
Assert.That(data["arrayValue"].ValueType, Is.EqualTo(KVValueType.Array));
94+
Assert.That(data["arrayOnSingleLine"].ValueType, Is.EqualTo(KVValueType.Array));
95+
Assert.That(data["arrayNoSpace"].ValueType, Is.EqualTo(KVValueType.Array));
96+
Assert.That(data["arrayMixedTypes"].ValueType, Is.EqualTo(KVValueType.Array));
97+
98+
// TODO: Test all the children values
9499
}
95100

96101
[Test]
@@ -99,7 +104,12 @@ public void DeserializesBinaryBlob()
99104
using var stream = TestDataHelper.OpenResource("TextKV3.binary_blob.kv3");
100105
var data = KVSerializer.Create(KVSerializationFormat.KeyValues3Text).Deserialize(stream);
101106

102-
Assert.True(false);
107+
Assert.That(data["array"].ValueType, Is.EqualTo(KVValueType.BinaryBlob));
108+
Assert.That(((KVBinaryBlob)data["array"]).Bytes, Is.EqualTo(new byte[]
109+
{
110+
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
111+
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xFF
112+
}));
103113
}
104114

105115
[Test]

ValveKeyValue/ValveKeyValue/Abstraction/KVObjectVisitor.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,13 @@ void VisitObject(string name, KVValue value, bool isArray)
2929
listener.OnObjectEnd();
3030
break;
3131

32-
case KVValueType.BinaryBlob:
33-
// TODO: write binary blobs
34-
break;
35-
3632
case KVValueType.Array:
3733
listener.OnArrayStart(name, value.Flag);
3834
VisitArray((IEnumerable<KVValue>)value);
3935
listener.OnArrayEnd();
4036
break;
4137

38+
case KVValueType.BinaryBlob: // TODO: Should binary blobs have their own method?
4239
case KVValueType.FloatingPoint:
4340
case KVValueType.Int32:
4441
case KVValueType.Pointer:

ValveKeyValue/ValveKeyValue/Deserialization/KeyValues3/KV3TextReader.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,11 @@ void ReadText(string text)
181181

182182
void ReadBinaryBlob(string text)
183183
{
184-
var bytes = Utils.ParseHexStringAsByteArray(text);
185-
//var value = new KVObjectValue<byte[]>(bytes, KVValueType.BinaryBlob);
186-
var value = new KVObjectValue<byte>(0x00, KVValueType.BinaryBlob); // TODO: wrong
187-
value.Flag = stateMachine.GetAndResetFlag();
184+
var bytes = Utils.ParseHexStringAsByteArrayNoReverse(text);
185+
var value = new KVBinaryBlob(bytes)
186+
{
187+
Flag = stateMachine.GetAndResetFlag()
188+
};
188189

189190
switch (stateMachine.Current)
190191
{
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using System;
2+
using System.Text;
3+
4+
namespace ValveKeyValue
5+
{
6+
public class KVBinaryBlob : KVValue
7+
{
8+
public byte[] Bytes { get; }
9+
10+
public override KVValueType ValueType => KVValueType.BinaryBlob;
11+
12+
public KVBinaryBlob(byte[] value)
13+
{
14+
Bytes = value;
15+
}
16+
17+
#region IConvertible
18+
19+
public override TypeCode GetTypeCode()
20+
{
21+
throw new NotSupportedException();
22+
}
23+
24+
public override bool ToBoolean(IFormatProvider provider)
25+
{
26+
throw new NotSupportedException();
27+
}
28+
29+
public override byte ToByte(IFormatProvider provider)
30+
{
31+
throw new NotSupportedException();
32+
}
33+
34+
public override char ToChar(IFormatProvider provider)
35+
{
36+
throw new NotSupportedException();
37+
}
38+
39+
public override DateTime ToDateTime(IFormatProvider provider)
40+
{
41+
throw new NotSupportedException();
42+
}
43+
44+
public override decimal ToDecimal(IFormatProvider provider)
45+
{
46+
throw new NotSupportedException();
47+
}
48+
49+
public override double ToDouble(IFormatProvider provider)
50+
{
51+
throw new NotSupportedException();
52+
}
53+
54+
public override short ToInt16(IFormatProvider provider)
55+
{
56+
throw new NotSupportedException();
57+
}
58+
59+
public override int ToInt32(IFormatProvider provider)
60+
{
61+
throw new NotSupportedException();
62+
}
63+
64+
public override long ToInt64(IFormatProvider provider)
65+
{
66+
throw new NotSupportedException();
67+
}
68+
69+
public override sbyte ToSByte(IFormatProvider provider)
70+
{
71+
throw new NotSupportedException();
72+
}
73+
74+
public override float ToSingle(IFormatProvider provider)
75+
{
76+
throw new NotSupportedException();
77+
}
78+
79+
public override string ToString(IFormatProvider provider)
80+
=> ToString();
81+
82+
public override object ToType(Type conversionType, IFormatProvider provider)
83+
{
84+
throw new NotSupportedException();
85+
}
86+
87+
public override ushort ToUInt16(IFormatProvider provider)
88+
{
89+
throw new NotSupportedException();
90+
}
91+
92+
public override uint ToUInt32(IFormatProvider provider)
93+
{
94+
throw new NotSupportedException();
95+
}
96+
97+
public override ulong ToUInt64(IFormatProvider provider)
98+
{
99+
throw new NotSupportedException();
100+
}
101+
102+
#endregion
103+
104+
public override string ToString()
105+
{
106+
var builder = new StringBuilder();
107+
108+
foreach (var oneByte in Bytes)
109+
{
110+
builder.Append(oneByte.ToString("X2"));
111+
builder.Append(' ');
112+
}
113+
114+
// Remove final space
115+
if (builder.Length > 1)
116+
{
117+
builder.Length -= 1;
118+
}
119+
120+
return builder.ToString();
121+
}
122+
}
123+
}

ValveKeyValue/ValveKeyValue/Serialization/KeyValues3/KV3TextSerializer.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
4+
using System.Drawing;
35
using System.IO;
46
using System.Text;
57
using ValveKeyValue.Abstraction;
@@ -142,6 +144,9 @@ void WriteValue(KVValue value)
142144

143145
switch (value.ValueType)
144146
{
147+
case KVValueType.BinaryBlob:
148+
WriteBinaryBlob((KVBinaryBlob)value);
149+
break;
145150
case KVValueType.Boolean:
146151
if ((bool)value)
147152
{
@@ -166,6 +171,49 @@ void WriteValue(KVValue value)
166171
}
167172
}
168173

174+
void WriteBinaryBlob(KVBinaryBlob value)
175+
{
176+
// TODO: Verify this against Valve
177+
if (value.Bytes.Length > 32)
178+
{
179+
writer.WriteLine();
180+
WriteIndentation();
181+
}
182+
183+
writer.Write('#');
184+
writer.Write('[');
185+
writer.WriteLine();
186+
indentation++;
187+
WriteIndentation();
188+
189+
var count = 0;
190+
191+
foreach (var oneByte in value.Bytes)
192+
{
193+
writer.Write(oneByte.ToString("X2"));
194+
195+
if (++count % 32 == 0)
196+
{
197+
writer.WriteLine();
198+
WriteIndentation();
199+
}
200+
else if (count != value.Bytes.Length)
201+
{
202+
writer.Write(' ');
203+
}
204+
}
205+
206+
indentation--;
207+
208+
if (count % 32 != 0)
209+
{
210+
writer.WriteLine();
211+
WriteIndentation();
212+
}
213+
214+
writer.Write(']');
215+
}
216+
169217
void WriteIndentation()
170218
{
171219
if (indentation == 0)

ValveKeyValue/ValveKeyValue/Utils.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ namespace ValveKeyValue
55
{
66
internal class Utils
77
{
8+
// TODO: Need to figure out whether Array.Reverse is actually correct for kv1
9+
public static byte[] ParseHexStringAsByteArrayNoReverse(string hexadecimalRepresentation)
10+
{
11+
Require.NotNull(hexadecimalRepresentation, nameof(hexadecimalRepresentation));
12+
13+
var data = new byte[hexadecimalRepresentation.Length / 2];
14+
for (var i = 0; i < data.Length; i++)
15+
{
16+
var currentByteText = hexadecimalRepresentation.Substring(i * 2, 2);
17+
data[i] = byte.Parse(currentByteText, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
18+
}
19+
20+
return data;
21+
}
22+
823
public static byte[] ParseHexStringAsByteArray(string hexadecimalRepresentation)
924
{
1025
Require.NotNull(hexadecimalRepresentation, nameof(hexadecimalRepresentation));

0 commit comments

Comments
 (0)