beth-parser
Version:
Parser library for Bethesda's Creation Engine data files
123 lines • 4.63 kB
JavaScript
;
exports.__esModule = true;
var zlib = require("zlib");
var ModfileDefs_1 = require("../ModfileDefs");
var utils_1 = require("../../utils");
/**
* Basic types used by the parser.
*/
var _a = utils_1.defineTypeTags("GRUP", "OFST", "XXXX"), GRUP = _a.GRUP, OFST = _a.OFST, XXXX = _a.XXXX;
/**
* Simple parser for Bethesda's ESM/ESP modfiles.
* Parser reads provided data source and processes entries via provided entry handler.
*/
var ModfileParser = /** @class */ (function () {
function ModfileParser(source) {
this.source = source;
}
/**
* Start modfile parsing with the given handler.
* @param handler Top-level modfile handler.
*/
ModfileParser.prototype.parse = function (handler) {
while (this.parseNext(handler)) { /* no-op */ }
};
/**
* Parse next entry and return its size in bytes.
* @param handler Current modfile handler.
* @param assert Whether to signal error when there is no content.
* @returns Number of processed bytes or zero for EOF.
*/
ModfileParser.prototype.parseNext = function (handler, assert) {
if (assert === void 0) { assert = false; }
var buffer = this.source.read(24);
var type = buffer.length === 24 ? buffer.readUInt32LE(0) : null;
if (assert && !type) {
throw new Error("Unexpected end of source reached.");
}
else if (type === GRUP) {
return this.parseGroup(buffer, handler);
}
else if (type !== null) {
return this.parseRecord(type, buffer, handler) + 24;
}
return 0; // EOF
};
/**
* Parse group based entry and return its size (including header).
* @param buffer Buffer with group header.
* @param handler Modfile handler to use.
*/
ModfileParser.prototype.parseGroup = function (buffer, handler) {
var _this = this;
var size = buffer.readUInt32LE(4);
var label = buffer.readUInt32LE(8);
var type = buffer.readInt32LE(12);
var skip = size - 24;
handler.handleGroup(type, skip, label, function (handler) {
while (skip > 0) {
skip -= _this.parseNext(handler, true);
}
});
this.source.skip(skip);
return size;
};
/**
* Parse record based entry and return size of its data.
* @param type Record type.
* @param buffer Buffer with record header.
* @param handler Modfile handler to use.
* @returns Number of processed bytes.
*/
ModfileParser.prototype.parseRecord = function (type, buffer, handler) {
var _this = this;
var size = buffer.readUInt32LE(4);
var flags = buffer.readUInt32LE(8);
var formId = buffer.readUInt32LE(12);
var skip = size;
handler.handleRecord(type, size, flags, formId, function (handler) {
var buffer = _this.source.read(size);
if (flags & ModfileDefs_1.RecordFlag.ZLIB_COMPRESSED) {
buffer = zlib.inflateSync(buffer.subarray(4));
}
_this.parseFields(buffer, handler);
skip = 0;
});
this.source.skip(skip);
return size;
};
/**
* Parse field based entries.
* @param buffer Byffer with field header.
* @param handler Modfile handler to use.
* @returns Number of processed bytes.
*/
ModfileParser.prototype.parseFields = function (buffer, handler) {
var offset = 0;
while (offset < buffer.length) {
offset += this.parseField(buffer, offset, buffer.readUInt16LE(offset + 4), handler);
}
return buffer.length;
};
/**
* Parse field at the specified offset.
* @param buffer Data buffer all the field data.
* @param offset Offset of field data in the buffer.
* @param size Size of field data.
* @param handler Modfile handler to use.
*/
ModfileParser.prototype.parseField = function (buffer, offset, size, handler) {
var type = buffer.readUInt32LE(offset);
if (type === OFST) {
return buffer.length - offset;
}
else if (type === XXXX) {
return size + 6 + this.parseField(buffer, offset + 10, buffer.readUInt32LE(offset + 6), handler);
}
handler(type, size, buffer, offset + 6);
return size + 6;
};
return ModfileParser;
}());
exports["default"] = ModfileParser;
//# sourceMappingURL=ModfileParser.js.map