diff --git a/Gameloop.Vdf/VdfConvert.cs b/Gameloop.Vdf/VdfConvert.cs
index bb8a5bd..91f89c7 100644
--- a/Gameloop.Vdf/VdfConvert.cs
+++ b/Gameloop.Vdf/VdfConvert.cs
@@ -8,13 +8,18 @@ namespace Gameloop.Vdf
public static class VdfConvert
{
public static string Serialize(VToken value)
+ {
+ return Serialize(value, VdfSerializerSettings.Common);
+ }
+
+ public static string Serialize(VToken value, VdfSerializerSettings settings)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
StringBuilder stringBuilder = new StringBuilder(256);
StringWriter stringWriter = new StringWriter(stringBuilder, CultureInfo.InvariantCulture);
- (new VdfSerializer()).Serialize(stringWriter, value);
+ (new VdfSerializer(settings)).Serialize(stringWriter, value);
return stringWriter.ToString();
}
diff --git a/Gameloop.Vdf/VdfSerializer.cs b/Gameloop.Vdf/VdfSerializer.cs
index 8af95cc..41135a9 100644
--- a/Gameloop.Vdf/VdfSerializer.cs
+++ b/Gameloop.Vdf/VdfSerializer.cs
@@ -15,7 +15,7 @@ public VdfSerializer(VdfSerializerSettings settings)
public void Serialize(TextWriter textWriter, VToken value)
{
- using (VdfWriter vdfWriter = new VdfTextWriter(textWriter))
+ using (VdfWriter vdfWriter = new VdfTextWriter(textWriter, _settings))
value.WriteTo(vdfWriter);
}
diff --git a/Gameloop.Vdf/VdfStructure.cs b/Gameloop.Vdf/VdfStructure.cs
index bc77563..e3bc74b 100644
--- a/Gameloop.Vdf/VdfStructure.cs
+++ b/Gameloop.Vdf/VdfStructure.cs
@@ -10,5 +10,47 @@ public static class VdfStructure
// Conditionals
public const string ConditionalXbox360 = "$X360", ConditionalWin32 = "$WIN32";
public const string ConditionalWindows = "$WINDOWS", ConditionalOSX = "$OSX", ConditionalLinux = "$LINUX", ConditionalPosix = "$POSIX";
+
+ // Escapes
+ private const uint EscapeMapLength = 128;
+ private static readonly bool[] EscapeExistsMap;
+ private static readonly char[] EscapeMap, UnescapeMap;
+ private static readonly char[,] EscapeConversions =
+ {
+ { '\n', 'n' },
+ { '\t', 't' },
+ { '\v', 'v' },
+ { '\b', 'b' },
+ { '\r', 'r' },
+ { '\f', 'f' },
+ { '\a', 'a' },
+ { '\\', '\\' },
+ { '?' , '?' },
+ { '\'', '\'' },
+ { '\"', '\"' }
+ };
+
+ static VdfStructure()
+ {
+ EscapeExistsMap = new bool[EscapeMapLength];
+ EscapeMap = new char[EscapeMapLength];
+ UnescapeMap = new char[EscapeMapLength];
+
+ for (int index = 0; index < EscapeMapLength; index++)
+ EscapeMap[index] = UnescapeMap[index] = (char) index;
+
+ for (int index = 0; index < EscapeConversions.GetLength(0); index++)
+ {
+ char unescaped = EscapeConversions[index, 0], escaped = EscapeConversions[index, 1];
+
+ EscapeExistsMap[unescaped] = true;
+ EscapeMap[unescaped] = escaped;
+ UnescapeMap[escaped] = unescaped;
+ }
+ }
+
+ public static bool IsEscapable(char ch) => (ch < EscapeMapLength && EscapeExistsMap[ch]);
+ public static char GetEscape(char ch) => (ch < EscapeMapLength) ? EscapeMap[ch] : ch;
+ public static char GetUnescape(char ch) => (ch < EscapeMapLength) ? UnescapeMap[ch] : ch;
}
}
diff --git a/Gameloop.Vdf/VdfTextReader.cs b/Gameloop.Vdf/VdfTextReader.cs
index afae6a3..63ea580 100644
--- a/Gameloop.Vdf/VdfTextReader.cs
+++ b/Gameloop.Vdf/VdfTextReader.cs
@@ -6,20 +6,6 @@ namespace Gameloop.Vdf
public class VdfTextReader : VdfReader
{
private const int DefaultBufferSize = 1024;
- private static readonly char[][] EscapeConversions =
- {
- new[] { 'n' , '\n' },
- new[] { 't' , '\t' },
- new[] { 'v' , '\v' },
- new[] { 'b' , '\b' },
- new[] { 'r' , '\r' },
- new[] { 'f' , '\f' },
- new[] { 'a' , '\a' },
- new[] { '\\', '\\' },
- new[] { '?' , '?' },
- new[] { '\'', '\'' },
- new[] { '\"', '\"' },
- };
private readonly TextReader _reader;
private readonly char[] _charBuffer, _tokenBuffer;
@@ -60,7 +46,7 @@ public override bool ReadToken()
if (curChar == VdfStructure.Escape)
{
- _tokenBuffer[_tokenSize++] = !Settings.UsesEscapeSequences ? curChar : FindConversion(_charBuffer[++_charPos]);
+ _tokenBuffer[_tokenSize++] = !Settings.UsesEscapeSequences ? curChar : VdfStructure.GetUnescape(_charBuffer[++_charPos]);
_charPos++;
continue;
}
@@ -181,20 +167,6 @@ private bool EnsureBuffer()
return _charsLen != 0;
}
- ///
- /// Converts the given escape code to an escape character.
- ///
- /// The escape code.
- /// the escape character.
- private static char FindConversion(char ch)
- {
- foreach (char[] conv in EscapeConversions)
- if (conv[0] == ch)
- return conv[1];
-
- return ch;
- }
-
public override void Close()
{
base.Close();
diff --git a/Gameloop.Vdf/VdfTextWriter.cs b/Gameloop.Vdf/VdfTextWriter.cs
index 2c28725..1bba1ea 100644
--- a/Gameloop.Vdf/VdfTextWriter.cs
+++ b/Gameloop.Vdf/VdfTextWriter.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using System.Linq;
namespace Gameloop.Vdf
{
@@ -8,7 +9,9 @@ public class VdfTextWriter : VdfWriter
private readonly TextWriter _writer;
private int _indentationLevel;
- public VdfTextWriter(TextWriter writer)
+ public VdfTextWriter(TextWriter writer) : this(writer, VdfSerializerSettings.Default) { }
+
+ public VdfTextWriter(TextWriter writer, VdfSerializerSettings settings) : base(settings)
{
if (writer == null)
throw new ArgumentNullException(nameof(writer));
@@ -21,7 +24,7 @@ public override void WriteKey(string key)
{
AutoComplete(State.Key);
_writer.Write(VdfStructure.Quote);
- _writer.Write(key);
+ WriteEscapedString(key);
_writer.Write(VdfStructure.Quote);
}
@@ -29,7 +32,7 @@ public override void WriteValue(VValue value)
{
AutoComplete(State.Value);
_writer.Write(VdfStructure.Quote);
- _writer.Write(value);
+ WriteEscapedString(value.ToString());
_writer.Write(VdfStructure.Quote);
}
@@ -81,6 +84,26 @@ private void AutoComplete(State next)
CurrentState = next;
}
+ private void WriteEscapedString(string str)
+ {
+ if (!Settings.UsesEscapeSequences)
+ {
+ _writer.Write(str);
+ return;
+ }
+
+ foreach (char ch in str)
+ {
+ if (!VdfStructure.IsEscapable(ch))
+ _writer.Write(ch);
+ else
+ {
+ _writer.Write(VdfStructure.Escape);
+ _writer.Write(VdfStructure.GetEscape(ch));
+ }
+ }
+ }
+
public override void Close()
{
base.Close();
diff --git a/Gameloop.Vdf/VdfWriter.cs b/Gameloop.Vdf/VdfWriter.cs
index 363d828..b4d53fe 100644
--- a/Gameloop.Vdf/VdfWriter.cs
+++ b/Gameloop.Vdf/VdfWriter.cs
@@ -4,11 +4,16 @@ namespace Gameloop.Vdf
{
public abstract class VdfWriter : IDisposable
{
+ public VdfSerializerSettings Settings { get; }
public bool CloseOutput { get; set; }
protected internal State CurrentState { get; protected set; }
- protected VdfWriter()
+ protected VdfWriter() : this(VdfSerializerSettings.Default) { }
+
+ protected VdfWriter(VdfSerializerSettings settings)
{
+ Settings = settings;
+
CurrentState = State.Start;
CloseOutput = true;
}