UNPKG

simple-vdf3

Version:

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

154 lines (153 loc) 6.33 kB
"use strict"; // a simple parser for Valve's KeyValue format // https://developer.valvesoftware.com/wiki/KeyValues // // author: Rossen Georgiev, 2014-2016 // contributors: Tom Shaver, 2017 & Albin hedwall, 2023 Object.defineProperty(exports, "__esModule", { value: true }); exports.dump = exports.stringify = exports.parse = void 0; // 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 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'); } let lines = text.split('\n'); let obj = {}; let stack = [obj]; let expectBracket = false; let line = ''; let m = null; let reKV = new RegExp('^("((?:\\\\.|[^\\\\"])+)"|([a-z0-9\\-\\_]+))' + '([ \t]*(' + '"((?:\\\\.|[^\\\\"])*)(")?' + '|([a-z0-9\\-\\_]+)' + '))?'); let i = 0; let j = lines.length; for (; i < j; i++) { line = lines[i].trim(); // skip empty and comment lines if (line === '' || line[0] === '/') { 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; continue; } if (expectBracket) { throw new SyntaxError('VDF.parse: expected bracket on line ' + (i + 1)); } // one level back if (line[0] === '}') { stack.pop(); continue; } let done = false; // parse keyvalue pairs while (!done) { m = reKV.exec(line); if (m === null) { throw new SyntaxError('VDF.parse: invalid syntax on line ' + (i + 1)); } // qkey = 2 // key = 3 // qval = 6 // vq_end = 7 // val = 8 let key = (typeof m[2] !== 'undefined') ? m[2] : m[3]; let val = (typeof m[6] !== 'undefined') ? m[6] : m[8]; if (typeof val === 'undefined') { // 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] = {}; } stack.push(stack[stack.length - 1][key]); expectBracket = true; } else { if (!m[7] && !m[8]) { line += '\n' + lines[++i]; continue; } stack[stack.length - 1][key] = val; } done = true; } } if (stack.length !== 1) { throw new SyntaxError('VDF.parse: open parentheses somewhere'); } return obj; } exports.parse = parse; function _dump(obj, pretty, level, DUPLICATE_TOKEN) { let indent = '\t'; let buf = ''; let lineIndent = ''; if (pretty) { for (let i = 0; i < level; i++) { lineIndent += indent; } } for (let key in obj) { // 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, '"', finalKey, '"', indent, indent, '"', String(obj[key]), '"\n'].join(''); } } return buf; } function stringify(obj, pretty, DUPLICATE_TOKEN) { return _dump(obj, pretty, 0, DUPLICATE_TOKEN); } exports.stringify = stringify; exports.dump = stringify;