UNPKG

sc4

Version:

A command line utility for automating SimCity 4 modding tasks & modifying savegames

163 lines (162 loc) 4.11 kB
let val; export default function parseStringExemplar(str) { val = str; // Read the parent cohort. until('ParentCohort'); ws(); until('='); ws(); until(':'); ws(); let parent = [readHex(), readHex(), readHex()]; // Next hex we find is the prop count. const propCount = readHex(); until('\n'); let properties = []; for (let i = 0; i < propCount; i++) { properties.push(readProp()); } return { parent, properties, }; } function advance(n) { val = val.slice(n); } function until(token) { let index = val.indexOf(token); advance(index + token.length); } // Consumes whitespace, but only if follows. const whitespaceRegex = /\s+/; function ws() { let match = val.match(whitespaceRegex); if (!match) return; if (match.index === 0) { advance(match[0].length); } } const hexRegex = /0x[0-9a-f]+/i; function readHexString() { let match = val.match(hexRegex); if (!match) return undefined; advance(match.index + match[0].length); return match[0]; } function readHex() { let str = readHexString(); if (str === undefined) return undefined; return Number(str); } // Reads in a single property line. function readProp() { // Work on a per-line basis so that we never surpass a line and read stuff // from another property by accident. let index = val.indexOf('\n'); let line = val.slice(0, index); advance(index); let temp = val; val = line; let id = readHex(); until(':'); // Read a potential comment. ws(); let comment = readComment(); // Read the values. until('='); // Read the type. index = val.indexOf(':'); let type = val.slice(0, index); advance(index + 1); // Read the resp. index = val.indexOf(':'); let reps = Number(val.slice(0, index)); advance(index + 1); let value = readValue(type, reps); // Restore. val = temp; // Consume trailing whitespace. ws(); // Convert the string type to an actual type hint used by the prop. return { id, comment, type, value }; } const commentRegex = /^{"(.*)"}/; function readComment() { let match = val.match(commentRegex); if (!match) return; return match[1]; } const stringRegex = /{"(.*)"}/; function readValue(type, reps) { if (type === 'String') { let match = val.match(stringRegex); if (!match) return ''; advance(match.index + match[0].length); return match[1]; } if (reps === 0) { return readSingleValue(type); } else { let out = []; for (let i = 0; i < reps; i++) { out.push(readSingleValue(type)); } return out; } } function readSingleValue(type) { switch (type) { case 'Uint32': case 'Uint16': case 'Uint8': return readHex(); case 'Float32': return readFloat(); case 'Sint64': return readBigInt(); case 'Sint32': return readInt32(); case 'Bool': return readBoolean(); default: throw new Error('Unknown type "' + type + '"!'); } } const floatRegex = /([+-]?\d+(\.\d+)?)/; function readFloat() { let match = val.match(floatRegex); if (!match) return undefined; advance(match.index + match[0].length); return Number(match[1]); } function readInt32() { let hex = readHexString(); if (hex === undefined) return undefined; return Number(hex); } function readBigInt() { let hex = readHexString(); if (hex === undefined) return undefined; if (typeof BigInt === 'undefined') { throw new Error('Your platform does not support the BigInt primitive!'); } return BigInt(hex); } const boolRegex = /(true|false)/i; function readBoolean() { let match = val.match(boolRegex); if (!match) return undefined; advance(match.index + match[0].length); return String(match[0]).toLowerCase() === 'true'; }