using System.Text; namespace CVRManager.Services; public static class Vdf { public static Dictionary Parse(string text, int start = 0) { var pos = start; var root = new Dictionary(StringComparer.OrdinalIgnoreCase); var key = ReadToken(text, ref pos); if (key == null) return root; var next = Peek(text, pos); if (next == '{') { pos = text.IndexOf('{', pos) + 1; root[key] = ParseBlock(text, ref pos); } return root; } public static Dictionary ParseDocument(string text) { var pos = 0; return ParseBlock(text, ref pos, true); } static Dictionary ParseBlock(string text, ref int pos, bool toEnd = false) { var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); while (true) { SkipSpace(text, ref pos); if (pos >= text.Length) return dict; if (text[pos] == '}') { pos++; return dict; } var key = ReadToken(text, ref pos); if (key == null) return dict; SkipSpace(text, ref pos); if (pos < text.Length && text[pos] == '{') { pos++; dict[key] = ParseBlock(text, ref pos); } else { var val = ReadToken(text, ref pos); if (val == null) return dict; dict[key] = val; } } } static char Peek(string text, int pos) { SkipSpace(text, ref pos); return pos < text.Length ? text[pos] : '\0'; } static void SkipSpace(string text, ref int pos) { while (pos < text.Length) { if (char.IsWhiteSpace(text[pos])) pos++; else if (text[pos] == '/' && pos + 1 < text.Length && text[pos + 1] == '/') while (pos < text.Length && text[pos] != '\n') pos++; else break; } } static string? ReadToken(string text, ref int pos) { SkipSpace(text, ref pos); if (pos >= text.Length || text[pos] != '"') return null; pos++; var sb = new StringBuilder(); while (pos < text.Length && text[pos] != '"') { if (text[pos] == '\\' && pos + 1 < text.Length) { pos++; sb.Append(text[pos] switch { 'n' => '\n', 't' => '\t', var c => c }); } else sb.Append(text[pos]); pos++; } pos++; return sb.ToString(); } public static Dictionary? Dict(this Dictionary d, string key) => d.TryGetValue(key, out var v) ? v as Dictionary : null; public static string Str(this Dictionary d, string key) => d.TryGetValue(key, out var v) && v is string s ? s : ""; }