UNPKG

@perion/net

Version:

A few networking utilities for MapleStory server development

263 lines (262 loc) 6.46 kB
/** * Parses binary packet Buffers in little endian * @class * @memberof module:@perion/net */ class Parser { /** * @constructor * @param {Buffer} data The input data Buffer */ constructor(data) { this.data = data; this.offset = 0; this.parsed = []; } /** * * Static constructor * @static * @param {Buffer} data The input data Buffer * @return {Parser} */ static from(data) { return new Parser(data); } /** * Reads a byte * @return {Parser} Returns a reference to the current Parser */ byte() { const byte = this.data.readIntLE(this.offset, 1); this.offset += 1; this.parsed.push(byte); return this; } /** * Reads a boolean * @return {Parser} Returns a reference to the current Parser */ bool() { const bool = this.byte().get(); this.parsed.push(bool === 1 ? true : false); return this; } /** * Reads an unsigned byte * @return {Parser} Returns a reference to the current Parser */ ubyte() { const uByte = this.data.readUIntLE(this.offset, 1); this.offset += 1; this.parsed.push(uByte); return this; } /** * Reads a short * @return {Parser} Returns a reference to the current Parser */ short() { const short = this.data.readInt16LE(this.offset); this.offset += 2; this.parsed.push(short); return this; } /** * Reads an unsigned short * @return {Parser} Returns a reference to the current Parser */ ushort() { const uShort = this.data.readUInt16LE(this.offset); this.offset += 2; this.parsed.push(uShort); return this; } /** * Reads a character * @return {Parser} Returns a reference to the current Parser */ char() { this.parsed.push(String.fromCharCode(97 + this.short().get())); return this; } /** * Reads an integer * @return {Parser} Returns a reference to the current Parser */ int() { const int = this.data.readInt32LE(this.offset); this.offset += 4; this.parsed.push(int); return this; } /** * Reads an unsigned integer * @return {Parser} Returns a reference to the current Parser */ uint() { const uInt = this.data.readUInt32LE(this.offset); this.offset += 4; this.parsed.push(uInt); return this; } /** * Reads a long * @return {Parser} Returns a reference to the current Parser */ long() { const long = this.data.readBigInt64LE(this.offset); this.offset += 8; this.parsed.push(long); return this; } /** * Reads an unsigned long * @return {Parser} Returns a reference to the current Parser */ ulong() { const uLong = this.data.readBigUInt64LE(this.offset); this.offset += 8; this.parsed.push(uLong); return this; } /** * Reads a single float * @return {Parser} Returns a reference to the current Parser */ float() { const float = this.data.readFloatLE(this.offset); this.offset += 4; this.parsed.push(float); return this; } /** * Reads a double * @return {Parser} Returns a reference to the current Parser */ double() { const double = this.data.readDoubleLE(this.offset); this.offset += 8; this.parsed.push(double); return this; } /** * Reads an ASCII string * @param {number} length Length of the string * @return {Parser} Returns a reference to the current Parser */ ascii({length}) { if (typeof length === 'string') { length = Number(length); } const stringBuffer = Buffer.alloc(length); for (let i = 0; i < length; i++) { stringBuffer[i] = this.byte().get(); } this.parsed.push(stringBuffer.toString('ascii')); return this; } /** * Reads a null terminated ASCII string * @return {Parser} Returns a reference to the current Parser */ nullascii() { const stringArr = []; const done = false; /** eslint */ while (!done) { const byte = this.byte().get(); /** Read until 0 */ if (byte === 0) break; stringArr.push(byte); } this.parsed.push(Buffer.from(stringArr).toString('ascii')); return this; } /** * Reads a MapleStory ASCII string * @return {Parser} Returns a reference to the current Parser */ mapleascii() { const length = this.short().get(); this.parsed.push(this.ascii({length}).get()); return this; } /** * Reads a position (x, y) * @return {Parser} Returns a reference to the current Parser */ pos() { const x = this.short().get(); const y = this.short().get(); this.parsed.push({x, y}); return this; } /** * Reads a specified length * @param {number} length * @return {Parser} Returns a reference to the current Parser */ read(length) { const ret = Buffer.alloc(length); for (let i = 0; i < length; i++) { ret[i] = this.byte().get(); } this.parsed.push(ret); return this; } /** * Skips the cursor a specified length * @param {number} length * @return {Parser} Returns a reference to the current Parser */ skip(length) { this.offset += length; return this; } /** * Moves the cursor to a specified byte offset * @param {number} offset * @return {Parser} Returns a reference to the current Parser */ seek(offset) { this.offset = offset; return this; } /** * Collects the parsed values and maps them to the input array strings * Will clear the parsed values * @param {Array<string>} fieldNames Names mapped to values * @return {Map<string, any>} The object with {name:value} */ collect(fieldNames) { if (fieldNames.length !== this.parsed.length) return false; const ret = {}; const length = fieldNames.length; for (let i = 0; i < length; i++) { ret[fieldNames[i]] = this.parsed[i]; } this.parsed = []; return ret; } /** * Gets the last parsed value, removes it from the parsed list. * Set the option flag removeParsed=false to override * @param {Object} options {removeParsed} * @return {any} Any piece of data */ get(removeParsed=true) { if (!removeParsed) return this.parsed[this.parsed.length - 1]; const latest = this.parsed.pop(); return latest; } /** * Parses a schema * @param {Object} schema * @return {Parser} */ parse(schema) { for (const type of schema) { if (type === 'ascii') this.ascii('test') } } } module.exports = Parser;