mirror of
https://github.com/a-sync/game-server-watcher.git
synced 2026-07-22 21:53:35 -04:00
283 lines
6.3 KiB
JavaScript
283 lines
6.3 KiB
JavaScript
|
|
/**
|
|
* Module dependencies.
|
|
*/
|
|
|
|
var Buffer = require('buffer').Buffer;
|
|
var inherits = require('util').inherits;
|
|
var Transform = require('readable-stream').Transform;
|
|
|
|
/**
|
|
* Expose `Parser`.
|
|
*/
|
|
|
|
module.exports = Parser;
|
|
|
|
/**
|
|
* Create a new `Parser`.
|
|
*
|
|
* @return {Parser}
|
|
* @api public
|
|
*/
|
|
|
|
function Parser () {
|
|
if (!(this instanceof Parser)) return new Parser();
|
|
|
|
Transform.call(this, {
|
|
objectMode : true
|
|
});
|
|
|
|
this.steps = [];
|
|
this.idx = 0;
|
|
this.res = {};
|
|
this.offset = 0;
|
|
this.buf = null;
|
|
}
|
|
|
|
inherits(Parser, Transform);
|
|
|
|
/**
|
|
* Transform callback.
|
|
*
|
|
* @param {Buffer} chunk
|
|
* @param {String} encoding
|
|
* @param {Function} done
|
|
* @api private
|
|
*/
|
|
|
|
Parser.prototype._transform = function (chunk, encoding, done) {
|
|
if (this.buf) {
|
|
chunk = Buffer.concat([this.buf, chunk]);
|
|
this.buf = null;
|
|
}
|
|
|
|
while (true) {
|
|
var broken = false;
|
|
for (var i = 0; i < this.steps.length; i++) {
|
|
var step = this.steps[i];
|
|
if (this.idx == i) {
|
|
var stepLength;
|
|
|
|
if (typeof step.length === 'number') {
|
|
stepLength = step.length;
|
|
} else if (typeof step.length === 'string') {
|
|
stepLength = this.res[step.length];
|
|
} else if (typeof step.chunkLength == 'number') {
|
|
if (typeof step.iterations === 'number') {
|
|
stepLength = step.iterations * step.chunkLength;
|
|
} else if (typeof step.iterations === 'string') {
|
|
stepLength = this.res[step.iterations] * step.chunkLength;
|
|
}
|
|
}
|
|
|
|
if (chunk.length < stepLength + this.offset) {
|
|
broken = true;
|
|
break;
|
|
}
|
|
|
|
this.res[step.name] = step.fn.apply(this.res, [chunk, this.offset]);
|
|
this.offset += stepLength;
|
|
this.idx++;
|
|
}
|
|
|
|
if (i === this.steps.length - 1) {
|
|
this.push(this.res);
|
|
this.res = {};
|
|
this.idx = 0;
|
|
}
|
|
}
|
|
if (broken) break;
|
|
}
|
|
|
|
if (chunk.length != this.offset) {
|
|
var dif = Math.abs(chunk.length - this.offset);
|
|
this.buf = new Buffer(dif);
|
|
chunk.copy(this.buf, 0, chunk.length - dif);
|
|
}
|
|
|
|
this.offset = 0;
|
|
|
|
done();
|
|
};
|
|
|
|
/**
|
|
* Register a parser function.
|
|
*
|
|
* @param {String} name
|
|
* @param {Number|String} length
|
|
* @param {Function} fn
|
|
* @return {Parser}
|
|
* @api public
|
|
*/
|
|
|
|
Parser.prototype.next = function (name, length, fn) {
|
|
this.steps.push({ name : name, length : length, fn : fn });
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Loop `length` times and save in `name` what `fn` registers.
|
|
*
|
|
* @param {String} name
|
|
* @param {Number|String} length
|
|
* @param {Function} fn
|
|
* @return {Parser}
|
|
* @api public
|
|
*/
|
|
|
|
Parser.prototype.loop = function (name, length, fn) {
|
|
// Get static chunk length
|
|
var parse = Parser();
|
|
fn(parse);
|
|
var chunkLength = parse.chunkLength();
|
|
|
|
this.steps.push({
|
|
name : name,
|
|
iterations : length,
|
|
chunkLength : chunkLength,
|
|
fn : function (chunk, offset) {
|
|
// let `fn` add parsing rules
|
|
var parse = Parser();
|
|
fn(parse);
|
|
|
|
// parse.on('data') fires synchronously
|
|
var res = [];
|
|
parse.on('data', function (data) {
|
|
res.push(data);
|
|
});
|
|
|
|
// iterations * chunkLength
|
|
if (typeof length === 'string') length = this[length];
|
|
length = length * parse.chunkLength();
|
|
|
|
// write to the parser
|
|
var buf = new Buffer(length);
|
|
chunk.copy(buf, 0, offset, offset + length);
|
|
parse.write(buf);
|
|
|
|
return res;
|
|
}
|
|
});
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Get the byte length of all steps together.
|
|
*
|
|
* @return {Number}
|
|
* @api private
|
|
*/
|
|
|
|
Parser.prototype.chunkLength = function () {
|
|
return this.steps.reduce(function (acc, step) {
|
|
return acc + step.length;
|
|
}, 0);
|
|
};
|
|
|
|
/**
|
|
* API sugar.
|
|
*/
|
|
|
|
Parser.prototype.string = function (name, length, encoding) {
|
|
if (!encoding) encoding = 'utf8';
|
|
var self = this;
|
|
return self.next(name, length, function (chunk, offset) {
|
|
var len = (typeof length === 'string') ? this[length] : length;
|
|
return chunk.toString(encoding, offset, offset + len);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.buffer = function (name, length) {
|
|
var self = this;
|
|
return self.next(name, length, function (chunk, offset) {
|
|
var len = (typeof length === 'string') ? this[length] : length;
|
|
var buf = new Buffer(len);
|
|
chunk.copy(buf, 0, offset, offset + len);
|
|
return buf;
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readUInt8 = function (name) {
|
|
return this.next(name, 1, function (chunk, offset) {
|
|
return chunk.readUInt8(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readUInt16LE = function (name) {
|
|
return this.next(name, 2, function (chunk, offset) {
|
|
return chunk.readUInt16LE(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readUInt16BE = function (name) {
|
|
return this.next(name, 2, function (chunk, offset) {
|
|
return chunk.readUInt16BE(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readUInt32LE = function (name) {
|
|
return this.next(name, 4, function (chunk, offset) {
|
|
return chunk.readUInt32LE(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readUInt32BE = function (name) {
|
|
return this.next(name, 4, function (chunk, offset) {
|
|
return chunk.readUInt32BE(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readInt8 = function (name) {
|
|
return this.next(name, 1, function (chunk, offset) {
|
|
return chunk.readInt8(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readInt16LE = function (name) {
|
|
return this.next(name, 2, function (chunk, offset) {
|
|
return chunk.readInt16LE(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readInt16BE = function (name) {
|
|
return this.next(name, 2, function (chunk, offset) {
|
|
return chunk.readInt16BE(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readInt32LE = function (name) {
|
|
return this.next(name, 4, function (chunk, offset) {
|
|
return chunk.readInt32LE(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readInt32BE = function (name) {
|
|
return this.next(name, 4, function (chunk, offset) {
|
|
return chunk.readInt32BE(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readFloatLE = function (name) {
|
|
return this.next(name, 4, function (chunk, offset) {
|
|
return chunk.readFloatLE(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readFloatBE = function (name) {
|
|
return this.next(name, 4, function (chunk, offset) {
|
|
return chunk.readFloatBE(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readDoubleLE = function (name) {
|
|
return this.next(name, 8, function (chunk, offset) {
|
|
return chunk.readDoubleLE(offset);
|
|
});
|
|
};
|
|
|
|
Parser.prototype.readDoubleBE = function (name) {
|
|
return this.next(name, 8, function (chunk, offset) {
|
|
return chunk.readDoubleBE(offset);
|
|
});
|
|
};
|