Hopefully VDF deserialization works now.

This commit is contained in:
That_One_Nerd 2023-05-09 09:46:55 -04:00
parent 1ee60cdf65
commit 7428ac9110
3 changed files with 174 additions and 55 deletions

View File

@ -17,11 +17,161 @@ public static class VdfConvert
{ "\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,
VdfOptions options) => SerializeNode(writer, node, name, options, 0);
public static void SerializeNode(StreamWriter writer, VdfNode? node, string name) =>
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, VdfOptions options)
{
@ -43,7 +193,7 @@ public static class VdfConvert
dictionary.Values.CopyTo(values, 0);
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;
}
@ -52,7 +202,7 @@ public static class VdfConvert
int index = 0;
foreach (object item in enumerable)
{
tree[SerializeObject(index, options)!] = ToNodeTree(item, options);
tree[SerializeObject(index)!] = ToNodeTree(item, options);
index++;
}
return tree;
@ -93,56 +243,5 @@ public static class VdfConvert
return tree;
}
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;
}
#endregion
}

View 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) { }
}

View File

@ -12,6 +12,17 @@ public class VdfSerializer
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)
{
VdfNode? nodeTree = VdfConvert.ToNodeTree(value, p_options);
@ -19,10 +30,11 @@ public class VdfSerializer
}
public void Serialize(Stream stream, VdfNode? parentNode, string parentNodeName)
{
long pos = stream.Position;
StreamWriter writer = new(stream, leaveOpen: !p_options.closeWhenFinished);
VdfConvert.SerializeNode(writer, parentNode, parentNodeName, p_options);
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);
}
}