UNPKG

rlr-parser

Version:
207 lines (181 loc) 6.76 kB
var Parser = require('binary-parser').Parser; //============================================================================== // Helper functions //============================================================================== // Parser choices needs an int value as key. // This function returns an int regarding to its property type. var selectDataType = function() { if(this.type === 'IntProperty') return 1; else if(this.type === 'ArrayProperty') return 2; else if(this.type === 'StrProperty' || this.type === 'NameProperty') return 3; else if(this.type === 'ByteProperty') return 4; else if(this.type === 'QWordProperty') return 5; else if(this.type === 'BoolProperty') return 6; else if(this.type === 'FloatProperty') return 7; }; // A function which returns 1 if the property name is "None", 0 if not. // Again the parser choices needs int keys. var isNone = function() { if(this.name === 'None') return 1; return 0; } // Helper function, which returns true, if the stream has read "None" // (or isNone() was 1). var readUntilNone = function(item, buf) { return item.name === 'None'; } // JS can not handle 64 bit integers, so the parser reads 2 32 bit integers. // This function converts the 32 bit integer from decimal to hex (little endian). var decToHex = function(item) { var hex = parseInt(item, 10).toString(16); var len = 8 - hex.length; if (len > 0) { for(len; len > 0; --len) hex = '0' + hex; } return hex; } //============================================================================== // Parsers // (All are little endian) //============================================================================== //------------------------------------------------------------------------------ // Value parsers //------------------------------------------------------------------------------ // Parses an 32 bit integer. var IntProperty = new Parser() .endianess('little') .int32('value'); // Parses a 32 bit integer length value of the following string // and the string itself with Null stripped. var StrProperty = new Parser() .endianess('little') .int32('vl') .string('value', { encoding: 'ascii', length: 'vl', stripNull: true }); // The byte property contains 2 strings prefixed by 2 32 bit integers as lengths // (Strange byte type ...) var ByteProperty = new Parser() .endianess('little') .int32('vl1') .string('value1', { encoding: 'ascii', length: 'vl1', stripNull: true }) .int32('vl2') .string('value2', { encoding: 'ascii', length: 'vl2', stripNull: true }); // Parses a 64 bit integer into 2 32 bit integers as hex values. var QWordProperty = new Parser() .endianess('little') .int32('hex2', { formatter: decToHex }) .int32('hex1', { formatter: decToHex }); // Parses 0 or 1 (1 byte). var BoolProperty = new Parser() .endianess('little') .bit8('value'); // Parses a 32 bit single precision value. var FloatProperty = new Parser() .endianess('little') .float('value'); //------------------------------------------------------------------------------ // Array Parsers //------------------------------------------------------------------------------ // This parser is like the property parser, just for the properties of the array. // See below for description. var ArrayPropertyDetail = new Parser() .endianess('little') .int32('nl') .string('name', { encoding: 'ascii', length: 'nl', stripNull: true }) .choice('more', { tag: isNone, choices: { 1: new Parser(), 0: new Parser() .endianess('little') .int32('tl') .string('type', { encoding: 'ascii', length: 'tl', stripNull: true }) .int32('unkn1') .int32('unkn2') .choice('details', { tag: selectDataType, choices: { 1: IntProperty, 3: StrProperty, 4: ByteProperty, 5: QWordProperty, 6: BoolProperty, 7: FloatProperty } }) } }); // This parser parses the array property, which has a length of elements // (like 4 representing 4 Goals) and a array of goal properties. var ArrayProperty = new Parser() .endianess('little') .int32('length') .array('array', { type: new Parser().array('part', { type: ArrayPropertyDetail, readUntil: readUntilNone }), length: 'length' }); //------------------------------------------------------------------------------ // Property parser //------------------------------------------------------------------------------ // This Parser parses the property section. It contains every single match // information like goals, teamsize, score, etc. // Every property has a name (string) prefixed by a length (int32). // Then a type information (string) prefixed by a length (int32). // And the value of the property (Selecting the right value parser). // Btw: After the property type there are 2 unknown 32 bit integers. var Property = new Parser() .endianess('little') .int32('nl') .string('name', { encoding: 'ascii', length: 'nl', stripNull: true }) .choice('more', { tag: isNone, choices: { 1: new Parser(), 0: new Parser() .endianess('little') .int32('tl') .string('type', { encoding: 'ascii', length: 'tl', stripNull: true }) .int32('unkn1') .int32('unkn2') .choice('details', { tag: selectDataType, choices: { 1: IntProperty, 2: ArrayProperty, 3: StrProperty, 4: ByteProperty, 5: QWordProperty, 6: BoolProperty, 7: FloatProperty } }) } }); //------------------------------------------------------------------------------ // Main parser and export //------------------------------------------------------------------------------ // This is the main parser which first parses the meta information of the replay. // Followed by the property section. // A small explanation: Strings are length prefixed, so the tags for the prefixes // are 2 chars like "tl" which means "type length". // Those values are only necessary to know how long the string will be. module.exports = new Parser() .endianess('little') .int32('identifier') .int32('crc') .int32('version_major') .int32('version_minor') .int32('tl') .string('type', { encoding: 'ascii', length: 'tl', stripNull: true }) .array('properties', { type: Property, readUntil: readUntilNone });