Skip to content
This repository was archived by the owner on Jun 24, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,30 @@

> A Simple library for Text-based VDF (Valve Data Format) (de)serialization.

Based on Rossen Georgiev's [simple-vdf](https://www.npmjs.com/package/simple-vdf).
Based on l3laze's [simple-vdf2](https://github.com/l3laze/vdf-parser) which is based on Rossen Georgiev's [simple-vdf](https://www.npmjs.com/package/simple-vdf).

## methods

### parse(string)
Parse a string containing VDF and returns an object
### parse(string[,string])
Parse a string containing VDF and returns an object.

First string is the VDF in its entirety
Second string is a special string that's passed to postpend to duplicate keys.

### stringify(obj[,boolean,string]) / dump(obj[,boolean,string])
Serializes an object to a string of VDF.

Object is the JSON to stringify.
Boolean is whether or not ot make the VDF output pretty (Indendentations)
String is an OPTIONAL string to strip from duplicate postpended keys

## modification in mstan variant

Backwards compatible with l3laze's iteration of simple-vdf.

By default, previous implementations of simple-vdf did NOT support any object that contained duplicate keys (for example, classloadoutpanel.res in TF2's HUD files contains multiple "animations" in the same object).

In this implementation, an optional string parameter is available in parse. This is something that will be postpended to any duplicate keys. (For example, if "animation" already exists in JSON, a second animation will become "animation-KEY-2", "animation-KEY-3"...). The same key will need to be provided in stringify/dump in order to strip that key back out. (Providing a different key from a parse will have undesirable results)

Without supplying the optional string, the old behavior will occur, in which duplicates will override each other (and the last one will be the one that results).

### stringify(obj) / dump(obj)
Serializes an object to a string of VDF
78 changes: 69 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,21 @@
// author: Rossen Popov, 2014-2016
// contributor: Tom Shaver, 2017

function parse (text) {

// duplicate token can be undefined. If it is, checks are skipped later on.
// it is expected for DUPLICATE_TOKEN to be a string identifier appended to
// duplicate keys
function parse (text, DUPLICATE_TOKEN) {
if (typeof text !== 'string') {
throw new TypeError('VDF.parse: Expecting parameter to be a string')
throw new TypeError('VDF.parse: Expecting text parameter to be a string')
}

// If duplicate token exists AND is not a string
if (DUPLICATE_TOKEN && typeof DUPLICATE_TOKEN !== 'string') {
throw new TypeError('VDF.parse: Expecting DUPLICATE_TOKEN parameter to be a string if defined')
}


var lines = text.split('\n')

var obj = {}
Expand Down Expand Up @@ -36,6 +46,14 @@ function parse (text) {
continue
}

// todo, implement case for handling #base 'includdes' that will
// import another ENTIRE file to import documents with.

// implemented for now to stop system from erroring out.
if(line[0] === '#' ) {
continue
}

// one level deeper
if (line[0] === '{') {
expectBracket = false
Expand Down Expand Up @@ -71,7 +89,34 @@ function parse (text) {
var val = (typeof m[ 6 ] !== 'undefined') ? m[ 6 ] : m[ 8 ]

if (typeof val === 'undefined') {
// chain (merge) duplicate key
// this is a duplicate key so we need to do special increment
// check to see if duplicate token is declared. if it's undefined, the user didn't set it/
// so skip this below operation. instead, proceed to the original behavior of merging.
if(DUPLICATE_TOKEN && stack[stack.length -1][ key ]) {
// if we are in here, the user has opted for not overriding duplicate keys

// we don't know how many duplicate keys exist, so we have to while loop
// and check our increments.
let newKeyFound = false; // by default, no idea where we are
let int = 2; // start at 2, the unmodified first one is "1".
let base = key; // the base of what the key variable should have each time

while(!newKeyFound) {
key = base + `-${DUPLICATE_TOKEN}-${int}`; // what the key shoud look like

// if this key has an assigned value already, keep going up
if( stack[stack.length -1][key] ) {
int++;
continue;
// this key does NOT have anything assigned. Assign it.
} else {
stack[stack.length -1][key] = {} // assign it
newKeyFound = true // break loop
}
}
}

// new key time!
if (!stack[stack.length - 1][ key ]) {
stack[stack.length - 1][ key ] = {}
}
Expand All @@ -98,7 +143,7 @@ function parse (text) {
return obj
}

function _dump (obj, pretty, level) {
function _dump (obj, pretty, level, DUPLICATE_TOKEN) {
if (typeof obj !== 'object') {
throw new TypeError('VDF.stringify: a key has value of type other than string or object')
}
Expand All @@ -114,24 +159,39 @@ function _dump (obj, pretty, level) {
}

for (let key in obj) {
if (typeof obj[ key ] === 'object') {
buf += [lineIndent, '"', key, '"\n', lineIndent, '{\n', _dump(obj[key], pretty, level + 1), lineIndent, '}\n'].join('')
// the key may not be the /binding/ key, for now we declare a variable
// and assign it to key.
// BELOW, with our if statement, we tentatively can change it.
let finalKey = key
// if a duplicate token was defined, check to see if this key has it.
// if it does, override the key in this context with only the original key value by taking index 0
if(DUPLICATE_TOKEN && key.includes(DUPLICATE_TOKEN)) finalKey = key.split(`-${DUPLICATE_TOKEN}-`)[0]

// in the below section, we update finalKey instead of key in this area because
// we want the stripped key as the key. BUT, we want the ORIGINAL keys data.
if (typeof obj[ finalKey ] === 'object') {
buf += [lineIndent, '"', finalKey, '"\n', lineIndent, '{\n', _dump(obj[key], pretty, level + 1, DUPLICATE_TOKEN), lineIndent, '}\n'].join('')
} else {
buf += [lineIndent, '"', key, '"', indent, indent, '"', String(obj[ key ]), '"\n'].join('')
buf += [lineIndent, '"', finalKey, '"', indent, indent, '"', String(obj[ key ]), '"\n'].join('')
}
}

return buf
}

function stringify (obj, pretty) {
function stringify (obj, pretty, DUPLICATE_TOKEN) {
if (typeof obj !== 'object') {
throw new TypeError('VDF.stringify: First input parameter is not an object')
}


if(DUPLICATE_TOKEN && typeof DUPLICATE_TOKEN !== 'string') {
throw new TypeError('VDF.stringify: Expecting DUPLICATE_TOKEN parameter to be a string if defined')
}

pretty = (typeof pretty === 'boolean' && pretty)

return _dump(obj, pretty, 0)
return _dump(obj, pretty, 0, DUPLICATE_TOKEN)
}

exports.parse = parse
Expand Down
15 changes: 10 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,26 @@
"name": "Tom Shaver",
"email": "l3l_aze@yahoo.com",
"url": "https://github.com/l3laze"
},
{
"name": "Matthew 'Gamemaster' Stanley",
"email": "github@1379.tech",
"url": "https://github.com/mstan"
}
],
"name": "simple-vdf2",
"name": "simple-vdf-mstan",
"description": "Package for (de)serialization of Valve's KeyValue format (VDF)",
"version": "1.2.1",
"version": "1.3.1",
"license": "MIT",
"homepage": "https://github.com/l3laze/vdf-parser",
"homepage": "https://github.com/mstan/vdf-parser",
"readmeFilename": "README.md",
"main": "index.js",
"repository": {
"type": "git",
"url": "git://github.com/l3laze/vdf-parser.git"
"url": "git://github.com/mstan/vdf-parser.git"
},
"bugs": {
"url": "https://github.com/l3laze/vdf-parser/issues"
"url": "https://github.com/mstan/vdf-parser/issues"
},
"scripts": {
"lint": "yarn standard",
Expand Down