Ready for the first beta release. #105
@ -17,11 +17,161 @@ public static class VdfConvert
|
|||||||
{ "\v", @"\v" }
|
{ "\v", @"\v" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#region DeserializeNode
|
||||||
|
public static VdfNode? DeserializeNode(StreamReader reader, VdfOptions options) =>
|
||||||
|
DeserializeNode(reader, options, out _, null);
|
||||||
|
|
||||||
|
private static VdfNode? DeserializeNode(StreamReader reader, VdfOptions options, out string name,
|
||||||
|
string? first)
|
||||||
|
{
|
||||||
|
string? header = first ?? (reader.ReadLine()?.Trim());
|
||||||
|
if (header is null || string.IsNullOrEmpty(header))
|
||||||
|
{
|
||||||
|
name = string.Empty;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] parts = SplitContent(header, options);
|
||||||
|
if (parts.Length > 2) throw new VdfSerializationException("Too many values in node.");
|
||||||
|
|
||||||
|
VdfNode node;
|
||||||
|
|
||||||
|
name = DeserializeString(parts[0], options);
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
object value = DeserializeObject(DeserializeString(parts[1], options));
|
||||||
|
node = new VdfSingleNode(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string? next = reader.ReadLine()?.Trim();
|
||||||
|
if (next is null) throw new VdfSerializationException("Expected starting '{', found end-of-file.");
|
||||||
|
else if (next != "{") throw new VdfSerializationException($"Expected starting '{{', found \"{next}\".");
|
||||||
|
VdfTreeNode tree = new();
|
||||||
|
string? current;
|
||||||
|
while ((current = reader.ReadLine()?.Trim()) is not null)
|
||||||
|
{
|
||||||
|
if (current == "}") break;
|
||||||
|
VdfNode? output = DeserializeNode(reader, options, out string subName, current);
|
||||||
|
if (output is null) throw new VdfSerializationException("Error deserializing sub-node.");
|
||||||
|
tree[subName] = output;
|
||||||
|
}
|
||||||
|
if (current is null) throw new VdfSerializationException("Reached end-of-file while deserializing group.");
|
||||||
|
node = tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object DeserializeObject(string content) =>
|
||||||
|
TypeParsers.ParseAll(content);
|
||||||
|
private static string DeserializeString(string content, VdfOptions options)
|
||||||
|
{
|
||||||
|
if (options.useQuotes)
|
||||||
|
{
|
||||||
|
if (!content.StartsWith('\"') || !content.EndsWith('\"'))
|
||||||
|
throw new VdfSerializationException("No quotes found around content.");
|
||||||
|
content = content[1..^1];
|
||||||
|
}
|
||||||
|
if (options.useEscapeCodes)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, string> escapeCode in p_escapeCodes.Reverse())
|
||||||
|
content = content.Replace(escapeCode.Value, escapeCode.Key);
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string[] SplitContent(string content, VdfOptions options)
|
||||||
|
{
|
||||||
|
content = content.Replace('\t', ' ');
|
||||||
|
if (options.useQuotes)
|
||||||
|
{
|
||||||
|
List<string> values = new();
|
||||||
|
string current = string.Empty;
|
||||||
|
bool inQuote = false;
|
||||||
|
for (int i = 0; i < content.Length; i++)
|
||||||
|
{
|
||||||
|
char c = content[i];
|
||||||
|
if (c == '\"' && !(i > 0 && content[i - 1] == '\\')) inQuote = !inQuote;
|
||||||
|
|
||||||
|
if (c == ' ' && !inQuote)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(current)) values.Add(current);
|
||||||
|
current = string.Empty;
|
||||||
|
}
|
||||||
|
else current += c;
|
||||||
|
}
|
||||||
|
if (inQuote) throw new VdfSerializationException("Reached end-of-line while inside quotations.");
|
||||||
|
if (!string.IsNullOrEmpty(current)) values.Add(current);
|
||||||
|
return values.ToArray();
|
||||||
|
}
|
||||||
|
else return content.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region SerializeNode
|
||||||
public static void SerializeNode(StreamWriter writer, VdfNode? node, string name,
|
public static void SerializeNode(StreamWriter writer, VdfNode? node, string name,
|
||||||
VdfOptions options) => SerializeNode(writer, node, name, options, 0);
|
VdfOptions options) => SerializeNode(writer, node, name, options, 0);
|
||||||
public static void SerializeNode(StreamWriter writer, VdfNode? node, string name) =>
|
public static void SerializeNode(StreamWriter writer, VdfNode? node, string name) =>
|
||||||
SerializeNode(writer, node, name, VdfOptions.Default, 0);
|
SerializeNode(writer, node, name, VdfOptions.Default, 0);
|
||||||
|
|
||||||
|
private static void SerializeNode(StreamWriter writer, VdfNode? node, string name,
|
||||||
|
VdfOptions options, int indentLevel)
|
||||||
|
{
|
||||||
|
if (node is null) return;
|
||||||
|
else if (node is VdfSingleNode single) SerializeSingleNode(writer, single, name, options, indentLevel);
|
||||||
|
else if (node is VdfTreeNode tree) SerializeTreeNode(writer, tree, name, options, indentLevel);
|
||||||
|
else throw new("Unknown node type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SerializeSingleNode(StreamWriter writer, VdfSingleNode node, string name,
|
||||||
|
VdfOptions options, int indentLevel)
|
||||||
|
{
|
||||||
|
string? serializedValue = SerializeObject(node.value);
|
||||||
|
if (serializedValue is null) return;
|
||||||
|
|
||||||
|
writer.Write(new string(' ', indentLevel));
|
||||||
|
writer.Write(SerializeString(name, options));
|
||||||
|
writer.Write(' ');
|
||||||
|
|
||||||
|
serializedValue = SerializeString(serializedValue, options);
|
||||||
|
writer.WriteLine(serializedValue);
|
||||||
|
}
|
||||||
|
private static void SerializeTreeNode(StreamWriter writer, VdfTreeNode node, string name,
|
||||||
|
VdfOptions options, int indentLevel)
|
||||||
|
{
|
||||||
|
if (node.SubNodeCount <= 0) return;
|
||||||
|
|
||||||
|
writer.Write(new string(' ', indentLevel));
|
||||||
|
writer.WriteLine(SerializeString(name, options));
|
||||||
|
writer.WriteLine(new string(' ', indentLevel) + '{');
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, VdfNode?> subNode in node)
|
||||||
|
SerializeNode(writer, subNode.Value, subNode.Key, options, indentLevel + options.indentSize);
|
||||||
|
|
||||||
|
writer.WriteLine(new string(' ', indentLevel) + '}');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? SerializeObject(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is null) return null;
|
||||||
|
return obj.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SerializeString(string content, VdfOptions options)
|
||||||
|
{
|
||||||
|
if (options.useEscapeCodes)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, string> escapeCode in p_escapeCodes)
|
||||||
|
content = content.Replace(escapeCode.Key, escapeCode.Value);
|
||||||
|
}
|
||||||
|
if (options.useQuotes) content = $"\"{content}\"";
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ToNodeTree
|
||||||
public static VdfNode? ToNodeTree(object? obj) => ToNodeTree(obj, VdfOptions.Default);
|
public static VdfNode? ToNodeTree(object? obj) => ToNodeTree(obj, VdfOptions.Default);
|
||||||
public static VdfNode? ToNodeTree(object? obj, VdfOptions options)
|
public static VdfNode? ToNodeTree(object? obj, VdfOptions options)
|
||||||
{
|
{
|
||||||
@ -43,7 +193,7 @@ public static class VdfConvert
|
|||||||
dictionary.Values.CopyTo(values, 0);
|
dictionary.Values.CopyTo(values, 0);
|
||||||
for (int i = 0; i < dictionary.Count; i++)
|
for (int i = 0; i < dictionary.Count; i++)
|
||||||
{
|
{
|
||||||
tree[SerializeObject(keys.GetValue(i), options)!] = ToNodeTree(values.GetValue(i), options);
|
tree[SerializeObject(keys.GetValue(i))!] = ToNodeTree(values.GetValue(i), options);
|
||||||
}
|
}
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
@ -52,7 +202,7 @@ public static class VdfConvert
|
|||||||
int index = 0;
|
int index = 0;
|
||||||
foreach (object item in enumerable)
|
foreach (object item in enumerable)
|
||||||
{
|
{
|
||||||
tree[SerializeObject(index, options)!] = ToNodeTree(item, options);
|
tree[SerializeObject(index)!] = ToNodeTree(item, options);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
return tree;
|
return tree;
|
||||||
@ -93,56 +243,5 @@ public static class VdfConvert
|
|||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
private static void SerializeNode(StreamWriter writer, VdfNode? node, string name,
|
|
||||||
VdfOptions options, int indentLevel)
|
|
||||||
{
|
|
||||||
if (node is null) return;
|
|
||||||
else if (node is VdfSingleNode single) SerializeSingleNode(writer, single, name, options, indentLevel);
|
|
||||||
else if (node is VdfTreeNode tree) SerializeTreeNode(writer, tree, name, options, indentLevel);
|
|
||||||
else throw new("Unknown node type.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SerializeSingleNode(StreamWriter writer, VdfSingleNode node, string name,
|
|
||||||
VdfOptions options, int indentLevel)
|
|
||||||
{
|
|
||||||
string? serializedValue = SerializeObject(node.value, options);
|
|
||||||
if (serializedValue is null) return;
|
|
||||||
|
|
||||||
writer.Write(new string(' ', indentLevel));
|
|
||||||
writer.Write(SerializeString(name, options));
|
|
||||||
writer.Write(' ');
|
|
||||||
|
|
||||||
serializedValue = SerializeString(serializedValue, options);
|
|
||||||
writer.WriteLine(serializedValue);
|
|
||||||
}
|
|
||||||
private static void SerializeTreeNode(StreamWriter writer, VdfTreeNode node, string name,
|
|
||||||
VdfOptions options, int indentLevel)
|
|
||||||
{
|
|
||||||
if (node.SubNodeCount <= 0) return;
|
|
||||||
|
|
||||||
writer.Write(new string(' ', indentLevel));
|
|
||||||
writer.WriteLine(SerializeString(name, options));
|
|
||||||
writer.WriteLine(new string(' ', indentLevel) + '{');
|
|
||||||
|
|
||||||
foreach (KeyValuePair<string, VdfNode?> subNode in node)
|
|
||||||
SerializeNode(writer, subNode.Value, subNode.Key, options, indentLevel + options.indentSize);
|
|
||||||
|
|
||||||
writer.WriteLine(new string(' ', indentLevel) + '}');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string? SerializeObject(object? obj, VdfOptions options)
|
|
||||||
{
|
|
||||||
if (obj is null) return null;
|
|
||||||
return obj.ToString() ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string SerializeString(string content, VdfOptions options)
|
|
||||||
{
|
|
||||||
if (options.useEscapeCodes)
|
|
||||||
foreach (KeyValuePair<string, string> escapeCode in p_escapeCodes)
|
|
||||||
content = content.Replace(escapeCode.Key, escapeCode.Value);
|
|
||||||
if (options.useQuotes) content = $"\"{content}\"";
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
8
SrcMod/Shell/Valve/VdfSerializationException.cs
Normal file
8
SrcMod/Shell/Valve/VdfSerializationException.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace SrcMod.Shell.Valve;
|
||||||
|
|
||||||
|
public class VdfSerializationException : Exception
|
||||||
|
{
|
||||||
|
public VdfSerializationException() : base() { }
|
||||||
|
public VdfSerializationException(string message) : base(message) { }
|
||||||
|
public VdfSerializationException(string message, Exception inner) : base(message, inner) { }
|
||||||
|
}
|
||||||
@ -12,6 +12,17 @@ public class VdfSerializer
|
|||||||
p_options = options;
|
p_options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VdfNode? Deserialize(Stream stream)
|
||||||
|
{
|
||||||
|
long pos = stream.Position;
|
||||||
|
StreamReader reader = new(stream, leaveOpen: !p_options.closeWhenFinished);
|
||||||
|
VdfNode? result = VdfConvert.DeserializeNode(reader, p_options);
|
||||||
|
reader.Close();
|
||||||
|
|
||||||
|
if (!p_options.closeWhenFinished && p_options.resetStreamPosition) stream.Seek(pos, SeekOrigin.Begin);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public void Serialize(Stream stream, object? value, string parentNodeName)
|
public void Serialize(Stream stream, object? value, string parentNodeName)
|
||||||
{
|
{
|
||||||
VdfNode? nodeTree = VdfConvert.ToNodeTree(value, p_options);
|
VdfNode? nodeTree = VdfConvert.ToNodeTree(value, p_options);
|
||||||
@ -19,10 +30,11 @@ public class VdfSerializer
|
|||||||
}
|
}
|
||||||
public void Serialize(Stream stream, VdfNode? parentNode, string parentNodeName)
|
public void Serialize(Stream stream, VdfNode? parentNode, string parentNodeName)
|
||||||
{
|
{
|
||||||
|
long pos = stream.Position;
|
||||||
StreamWriter writer = new(stream, leaveOpen: !p_options.closeWhenFinished);
|
StreamWriter writer = new(stream, leaveOpen: !p_options.closeWhenFinished);
|
||||||
VdfConvert.SerializeNode(writer, parentNode, parentNodeName, p_options);
|
VdfConvert.SerializeNode(writer, parentNode, parentNodeName, p_options);
|
||||||
writer.Close();
|
writer.Close();
|
||||||
|
|
||||||
if (!p_options.closeWhenFinished && p_options.resetStreamPosition) stream.Seek(0, SeekOrigin.Begin);
|
if (!p_options.closeWhenFinished && p_options.resetStreamPosition) stream.Seek(pos, SeekOrigin.Begin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user