{"id":20661652,"url":"https://github.com/valveresourceformat/valvekeyvalue","last_synced_at":"2026-04-05T11:01:11.839Z","repository":{"id":4295364,"uuid":"52661787","full_name":"ValveResourceFormat/ValveKeyValue","owner":"ValveResourceFormat","description":"📃 Next-generation Valve's key value framework for .NET","archived":false,"fork":false,"pushed_at":"2026-04-03T17:52:29.000Z","size":854,"stargazers_count":178,"open_issues_count":12,"forks_count":49,"subscribers_count":6,"default_branch":"master","last_synced_at":"2026-04-03T20:25:05.577Z","etag":null,"topics":["csharp","dotnet","source2","steam","valve"],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/ValveKeyValue/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ValveResourceFormat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["xPaw","kristiker"]}},"created_at":"2016-02-27T10:37:34.000Z","updated_at":"2026-04-03T17:52:34.000Z","dependencies_parsed_at":"2023-12-29T11:28:31.818Z","dependency_job_id":"71403ecf-f35b-4b0a-b3cc-b770cbc14391","html_url":"https://github.com/ValveResourceFormat/ValveKeyValue","commit_stats":null,"previous_names":["valveresourceformat/valvekeyvalue","steamdatabase/valvekeyvalue"],"tags_count":30,"template":false,"template_full_name":null,"purl":"pkg:github/ValveResourceFormat/ValveKeyValue","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ValveResourceFormat%2FValveKeyValue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ValveResourceFormat%2FValveKeyValue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ValveResourceFormat%2FValveKeyValue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ValveResourceFormat%2FValveKeyValue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ValveResourceFormat","download_url":"https://codeload.github.com/ValveResourceFormat/ValveKeyValue/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ValveResourceFormat%2FValveKeyValue/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31433044,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T08:13:15.228Z","status":"ssl_error","status_checked_at":"2026-04-05T08:13:11.839Z","response_time":75,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet","source2","steam","valve"],"created_at":"2024-11-16T19:10:45.134Z","updated_at":"2026-04-05T11:01:11.829Z","avatar_url":"https://github.com/ValveResourceFormat.png","language":"C#","readme":"\u003ch1 align=\"center\"\u003e\u003cimg src=\"./Misc/logo.png\" width=\"64\" height=\"64\" align=\"center\"\u003e Valve Key Value for .NET\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/ValveResourceFormat/ValveKeyValue/actions\" title=\"Build Status\"\u003e\u003cimg alt=\"Build Status\" src=\"https://img.shields.io/github/actions/workflow/status/ValveResourceFormat/ValveKeyValue/ci.yml?logo=github\u0026label=Build\u0026logoColor=ffffff\u0026style=for-the-badge\u0026branch=master\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://www.nuget.org/packages/ValveKeyValue/\" title=\"NuGet\"\u003e\u003cimg alt=\"NuGet\" src=\"https://img.shields.io/nuget/v/ValveKeyValue.svg?logo=nuget\u0026label=NuGet\u0026logoColor=ffffff\u0026color=004880\u0026style=for-the-badge\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://app.codecov.io/gh/ValveResourceFormat/ValveKeyValue\" title=\"Code Coverage\"\u003e\u003cimg alt=\"Code Coverage\" src=\"https://img.shields.io/codecov/c/github/ValveResourceFormat/ValveKeyValue/master?logo=codecov\u0026label=Coverage\u0026logoColor=ffffff\u0026color=F01F7A\u0026style=for-the-badge\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nKeyValues is a simple key-value pair format used by Valve in Steam and the Source engine for configuration files, game data, and more (`.vdf`, `.res`, `.acf`, etc.). This library aims to be fully compatible with Valve's various implementations of KeyValues format parsing (believe us, it's not consistent).\n\n# Core Type\n\nThe library is built around a single type:\n\n- **`KVObject`** (class) -- a value node. Can be a scalar (string, int, float, bool, etc.), a binary blob, an array, or a named collection of children. Keys (names) are stored in the parent container, not on the child -- similar to how JSON works. Implements `IReadOnlyDictionary\u003cstring, KVObject\u003e` and `IConvertible`.\n- **`KVDocument`** (class) -- a deserialized document containing a `Root` KVObject, a root key `Name`, and an optional `Header`. Has a read-only string indexer that delegates to `Root`, and an implicit conversion to `KVObject`.\n\nAll types are shared across KV1 and KV3 -- you can deserialize from one format and serialize to another. However, not all value types are supported by all formats:\n\n| Feature | KV1 Text | KV1 Binary | KV3 Text |\n|---------|----------|------------|----------|\n| Collections | Yes (list-backed, allows duplicate keys) | Yes (list-backed) | Yes (dict-backed, O(1) lookup) |\n| Arrays | Emulated as objects with numeric keys | No (throws) | Yes (native) |\n| Binary blobs | No | No (throws) | Yes (native) |\n| Scalars | Yes | Yes | Yes |\n| Flags | No | No | Yes |\n\nWhen constructing objects programmatically, use `KVObject.Collection()` (dict-backed) for general use and KV3 output, or `KVObject.ListCollection()` (list-backed) when you need duplicate keys or KV1 compatibility. Deserialization picks the appropriate backing store automatically.\n\n## KVObject\n\n### Constructing\n\n```csharp\n// Scalar values (typed constructors)\nvar obj = new KVObject(\"hello\");    // string\nvar obj = new KVObject(42);         // int\nvar obj = new KVObject(3.14f);      // float\nvar obj = new KVObject(true);       // bool\n\n// Implicit conversion from primitives\nKVObject obj = \"hello\";\nKVObject obj = 42;\n\n// Dictionary-backed collection (O(1) lookup, no duplicate keys)\nvar obj = KVObject.Collection();                     // empty\nvar obj = new KVObject();                            // same as above\n\n// List-backed collection (preserves insertion order, allows duplicate keys, for KV1)\nvar obj = KVObject.ListCollection();                 // empty\n\n// Build up children\nvar obj = new KVObject();\nobj[\"name\"] = \"Dota 2\";                              // implicit string -\u003e KVObject\nobj[\"appid\"] = 570;                                   // implicit int -\u003e KVObject\n\n// Array\nvar arr = KVObject.Array();                           // empty\nvar arr = KVObject.Array([ new KVObject(\"a\"), new KVObject(\"b\") ]); // from elements\n\n// Binary blob\nvar blob = KVObject.Blob(new byte[] { 0x01, 0x02, 0x03 });\n\n// Null value\nvar nul = KVObject.Null();\n```\n\n### Reading values\n\n```csharp\nKVDocument data = kv.Deserialize(stream);\n\n// Root key name (only on KVDocument)\nstring? rootName = data.Name;\n\n// String indexer returns KVObject (supports chaining)\nstring name = (string)data[\"config\"][\"name\"];\nint version = (int)data[\"version\"];\nfloat scale = (float)data[\"scale\"];\nbool enabled = (bool)data[\"settings\"][\"enabled\"];\n\n// Array elements by index\nfloat x = (float)data[\"position\"][0];\n\n// Access the root KVObject for full API (mutations, ContainsKey, etc.)\nKVObject root = data.Root;\n\n// Check existence (on the root KVObject)\nif (data.Root.ContainsKey(\"optional\")) { ... }\nif (data.Root.TryGetValue(\"optional\", out var child)) { ... }\n\n// Indexer throws KeyNotFoundException for missing keys\n// Use TryGetValue for safe access\n\n// Direct access to value properties (on KVObject)\nKVValueType type = data.Root.ValueType;\nKVFlag flag = data[\"texture\"].Flag;\nbyte[] bytes = data[\"blob\"].AsBlob();\n```\n\n### Modifying\n\n```csharp\n// Mutations require the Root KVObject (KVDocument indexer is read-only)\ndata.Root[\"name\"] = \"new name\";\ndata.Root[\"count\"] = 42;\n\n// Chained writes work (reference semantics, first lookup goes through KVDocument indexer)\ndata[\"config\"][\"resolution\"] = \"1920x1080\";\n\n// Add children to collections\ndata.Root.Add(\"newprop\", 42);      // implicit int -\u003e KVObject\ndata.Root.Add(\"text\", \"value\");    // implicit string -\u003e KVObject\n\n// Add elements to arrays\narr.Add(3.14f);                    // implicit float -\u003e KVObject\n\n// Remove\ndata.Root.Remove(\"deprecated\");\narr.RemoveAt(2);\ndata.Root.Clear();\n\n// Set flags directly\ndata[\"texture\"].Flag = KVFlag.Resource;\n```\n\n### Enumerating\n\n```csharp\n// KVObject implements IReadOnlyDictionary\u003cstring, KVObject\u003e\n// Keys are the child names, values are the child KVObjects\nforeach (var (key, child) in data.Root)\n{\n    Console.WriteLine($\"{key} = {(string)child}\");\n}\n\n// Keys and Values properties\nvar keys = data.Root.Keys;       // IEnumerable\u003cstring\u003e\nvar values = data.Root.Values;   // IEnumerable\u003cKVObject\u003e\n\n// Array elements have null keys\nforeach (var (key, element) in arrayObj)\n{\n    // key is null for array elements\n    Console.WriteLine((string)element);\n}\n\n// Values on arrays returns elements directly (no KVP wrapper)\nforeach (var element in arrayObj.Values)\n{\n    Console.WriteLine((string)element);\n}\n\n// Scalars yield nothing\nforeach (var child in scalarObj) { } // empty\n```\n\n# KeyValues1\n\nUsed by Steam and the Source engine.\n\n## Deserializing text\n\n### Basic deserialization\n```csharp\nvar stream = File.OpenRead(\"file.vdf\"); // or any other Stream\n\nvar kv = KVSerializer.Create(KVSerializationFormat.KeyValues1Text);\nKVDocument data = kv.Deserialize(stream);\n\nConsole.WriteLine(data[\"some key\"]);\n```\n\n### Typed deserialization\n```csharp\npublic class SimpleObject\n{\n    public string Name { get; set; }\n    public string Value { get; set; }\n}\n\nvar stream = File.OpenRead(\"file.vdf\"); // or any other Stream\n\nvar kv = KVSerializer.Create(KVSerializationFormat.KeyValues1Text);\nSimpleObject data = kv.Deserialize\u003cSimpleObject\u003e(stream);\n```\n\n### Options\nThe `Deserialize` method also accepts a `KVSerializerOptions` object.\n\nBy default, operating system specific conditionals are enabled based on the OS the code is running on (`RuntimeInformation`).\n\n`KVSerializerOptions` has the following options:\n\n* `Conditions` - List of conditions to use to match conditional values.\n* `HasEscapeSequences` - Whether the parser should translate escape sequences (e.g. `\\n`, `\\t`).\n* `EnableValveNullByteBugBehavior` - Whether invalid escape sequences should truncate strings rather than throwing an `InvalidDataException`.\n* `FileLoader` - Provider for referenced files with `#include` or `#base` directives.\n* `SkipHeader` - Whether to skip writing the KV3 header comment during serialization.\n\n```csharp\nvar options = new KVSerializerOptions\n{\n    HasEscapeSequences = true,\n};\noptions.Conditions.Clear(); // Remove default conditionals set by the library\noptions.Conditions.Add(\"X360WIDE\");\n\nvar stream = File.OpenRead(\"file.vdf\");\n\nvar kv = KVSerializer.Create(KVSerializationFormat.KeyValues1Text);\nvar data = kv.Deserialize(stream, options);\n```\n\n## Deserializing binary\n\nEssentially the same as text, just change `KeyValues1Text` to `KeyValues1Binary`.\n\n## Serializing to text\n\n### Dynamic serialization\n```csharp\nvar root = KVObject.ListCollection();\nroot.Add(\"Developer\", \"Valve Software\");\nroot.Add(\"Name\", \"Dota 2\");\nvar doc = new KVDocument(null, \"root object name\", root);\n\nusing var stream = File.OpenWrite(\"file.vdf\");\n\nvar kv = KVSerializer.Create(KVSerializationFormat.KeyValues1Text);\nkv.Serialize(stream, doc);\n```\n\n### Typed serialization\n```csharp\nclass DataObject\n{\n    public string Name { get; set; }\n\n    public string Developer { get; set; }\n\n    [KVProperty(\"description\")]\n    public string Summary { get; set; }\n\n    [KVIgnore]\n    public string ExtraData { get; set; }\n}\n\nvar data = new DataObject\n{\n    Developer = \"Valve Software\",\n    Name = \"Dota 2\",\n    Summary = \"Dota 2 is a complex game.\",\n    ExtraData = \"This will not be serialized.\"\n};\n\nusing var stream = File.OpenWrite(\"file.vdf\");\n\nvar kv = KVSerializer.Create(KVSerializationFormat.KeyValues1Text);\nkv.Serialize(stream, data, \"root object name\");\n```\n\n## Serializing to binary\n\nEssentially the same as text, just change `KeyValues1Text` to `KeyValues1Binary`.\n\n# KeyValues2 (Datamodel)\n\nThis library does not currently support KeyValues2 (Datamodel). If you need KV2/Datamodel support, use our fork of [Datamodel.NET](https://github.com/ValveResourceFormat/Datamodel.NET) instead.\n\n# KeyValues3\n\nUsed by the Source 2 engine.\n\n## Deserializing text\n\n```csharp\nvar stream = File.OpenRead(\"file.kv3\"); // or any other Stream\n\nvar kv = KVSerializer.Create(KVSerializationFormat.KeyValues3Text);\nKVDocument data = kv.Deserialize(stream);\n\nConsole.WriteLine(data[\"some key\"]);\n```\n\n## Serializing to text\n\n```csharp\nusing var stream = File.OpenWrite(\"file.kv3\");\n\nvar kv = KVSerializer.Create(KVSerializationFormat.KeyValues3Text);\nkv.Serialize(stream, data);\n```\n","funding_links":["https://github.com/sponsors/xPaw","https://github.com/sponsors/kristiker"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvalveresourceformat%2Fvalvekeyvalue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvalveresourceformat%2Fvalvekeyvalue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvalveresourceformat%2Fvalvekeyvalue/lists"}