UNPKG

node-insim

Version:

An InSim library for NodeJS with TypeScript support

407 lines (406 loc) 12.4 kB
import parseLFSMessage from 'parse-lfs-message'; import unicodeToLfs from 'unicode-to-lfs'; const magic = { // byte array A: { length: 1, pack(dv, value, offset, c) { if (Array.isArray(value)) { for (let i = 0; i < c; i++) { dv.setInt8(offset + i, value[0][i]); } } else { for (let i = 0; i < c; i++) { dv.setInt8(offset + i, [value][i]); } } }, unpack(dv, offset, c) { const r = []; for (let i = 0; i < c; i++) { r.push(dv.getInt8(offset + i)); } return [r]; }, }, // padding byte x: { length: 1, pack(dv, value, offset, c) { for (let i = 0; i < c; i++) { dv.setUint8(0, offset + i); } }, unpack(dv, offset, c) { const r = []; for (let i = 0; i < c; i++) { r.push(0); } return r; }, }, // char c: { length: 1, pack(dv, value, offset, c) { if (!Array.isArray(value)) value = [value]; for (let i = 0; i < c; i++) { dv.setUint8(offset + i, value[i].charCodeAt(0)); } }, unpack(dv, offset, c) { const r = []; for (let i = 0; i < c; i++) { r.push(String.fromCharCode(dv.getUint8(offset + i))); } return r; }, }, // LFS car name string C: { length: 4, pack(dv, value, offset) { if (!Array.isArray(value)) value = [value]; const carName = value[0]; if (isAlphaNumeric(carName[0]) && isAlphaNumeric(carName[1]) && isAlphaNumeric(carName[2]) && carName.length === 3) { for (let i = 0; i < 3; i++) { dv.setUint8(offset + i, carName[i].charCodeAt(0)); } } else { dv.setUint8(offset + 2, parseInt(`${carName[0]}${carName[1]}`, 16)); dv.setUint8(offset + 1, parseInt(`${carName[2]}${carName[3]}`, 16)); dv.setUint8(offset, parseInt(`${carName[4]}${carName[5]}`, 16)); } }, unpack(dv, offset) { const r = []; for (let i = 0; i < 4; i++) r.push(String.fromCharCode(dv.getUint8(offset + i))); if (isAlphaNumeric(r[0]) && isAlphaNumeric(r[1]) && isAlphaNumeric(r[2]) && r[3] === '\x00') { return [r.slice(0, -1).join('')]; } return [ [...new Uint8Array(dv.buffer.slice(offset, offset + 3))] .reverse() .map((x) => x.toString(16).toUpperCase().padStart(2, '0')) .join(''), ]; }, }, // signed char b: { length: 1, pack(dv, value, offset, c) { if (!Array.isArray(value)) value = [value]; for (let i = 0; i < c; i++) { dv.setInt8(offset + i, value[i]); } }, unpack(dv, offset, c) { const r = []; for (let i = 0; i < c; i++) { r.push(dv.getInt8(offset + i)); } return r; }, }, // unsigned char B: { length: 1, pack(dv, value, offset, c) { if (!Array.isArray(value)) value = [value]; for (let i = 0; i < c; i++) { dv.setUint8(offset + i, value[i]); } }, unpack(dv, offset, c) { const r = []; for (let i = 0; i < c; i++) { r.push(dv.getUint8(offset + i)); } return r; }, }, // signed short h: { length: 2, pack(dv, value, offset, c, littleendian) { if (!Array.isArray(value)) value = [value]; for (let i = 0; i < c; i++) { dv.setInt16(offset + i, value[i], littleendian); } }, unpack(dv, offset, c, littleendian) { const r = []; for (let i = 0; i < c; i++) { r.push(dv.getInt16(offset + i, littleendian)); } return r; }, }, // unsigned short H: { length: 2, pack(dv, value, offset, c, littleendian) { if (!Array.isArray(value)) value = [value]; for (let i = 0; i < c; i++) { dv.setUint16(offset + i, value[i], littleendian); } }, unpack(dv, offset, c, littleendian) { const r = []; for (let i = 0; i < c; i++) { r.push(dv.getUint16(offset + i, littleendian)); } return r; }, }, // signed long i: { length: 4, pack(dv, value, offset, c, littleendian) { if (!Array.isArray(value)) value = [value]; for (let i = 0; i < c; i++) { dv.setInt32(offset + i, value[i], littleendian); } }, unpack(dv, offset, c, littleendian) { const r = []; for (let i = 0; i < c; i++) { r.push(dv.getInt32(offset + i, littleendian)); } return r; }, }, // unsigned long I: { length: 4, pack(dv, value, offset, c, littleendian) { if (!Array.isArray(value)) value = [value]; for (let i = 0; i < c; i++) { dv.setUint32(offset + i, value[i], littleendian); } }, unpack(dv, offset, c, littleendian) { const r = []; for (let i = 0; i < c; i++) { r.push(dv.getUint32(offset + i, littleendian)); } return r; }, }, l: { length: 4, pack(dv, value, offset, c, littleendian) { if (!Array.isArray(value)) value = [value]; for (let i = 0; i < c; i++) { dv.setInt32(offset + i, value[i], littleendian); } }, unpack(dv, offset, c, littleendian) { const r = []; for (let i = 0; i < c; i++) { r.push(dv.getInt32(offset + i, littleendian)); } return r; }, }, // unsigned long L: { length: 4, pack(dv, value, offset, c, littleendian) { if (!Array.isArray(value)) value = [value]; for (let i = 0; i < c; i++) { dv.setUint32(offset + i, value[i], littleendian); } }, unpack(dv, offset, c, littleendian) { const r = []; for (let i = 0; i < c; i++) { r.push(dv.getUint32(offset + i, littleendian)); } return r; }, }, // char[] s: { length: 1, pack(dv, value, offset, c) { const val = unicodeToLfs(value[0], { isNullTerminated: false, length: c, }); for (let i = 0; i < c; i++) { let code = 0; if (i < val.length) code = val.charCodeAt(i); dv.setUint8(offset + i, code); } }, unpack(dv, offset, c) { const chars = []; const bytes = []; for (let i = 0; i < c; i++) { chars.push(String.fromCharCode(dv.getUint8(offset + i))); bytes.push(dv.getUint8(offset + i)); } return [[chars.join(''), parseLFSMessage(new Uint8Array(bytes))]]; }, }, // char[] - null-terminated S: { length: 1, pack(dv, value, offset, c) { const val = unicodeToLfs(value[0], { isNullTerminated: true, length: c, }); for (let i = 0; i < c; i++) { let code = 0; if (i < val.length) code = val.charCodeAt(i); dv.setUint8(offset + i, code); } }, unpack(dv, offset, c) { const chars = []; const bytes = []; for (let i = 0; i < c; i++) { chars.push(String.fromCharCode(dv.getUint8(offset + i))); bytes.push(dv.getUint8(offset + i)); } return [[chars.join(''), parseLFSMessage(new Uint8Array(bytes))]]; }, }, // float f: { length: 4, pack(dv, value, offset, c, littleendian) { if (!Array.isArray(value)) value = [value]; for (let i = 0; i < c; i++) { dv.setFloat32(offset + i, value[i], littleendian); } }, unpack(dv, offset, c, littleendian) { const r = []; for (let i = 0; i < c; i++) { r.push(dv.getFloat32(offset + i, littleendian)); } return r; }, }, // double d: { length: 8, pack(dv, value, offset, c, littleendian) { if (!Array.isArray(value)) value = [value]; for (let i = 0; i < c; i++) { dv.setFloat64(offset + i, value[i], littleendian); } }, unpack(dv, offset, c, littleendian) { const r = []; for (let i = 0; i < c; i++) { r.push(dv.getFloat64(offset + i, littleendian)); } return r; }, }, }; // pattern of stuff we're looking for const pattern = '(\\d+)?([AxcCbBhHsSfdiIlL])'; /** * Determine the size of arraybuffer we'd need * @internal */ const determineLength = function (fmt) { const re = new RegExp(pattern, 'g'); let m, sum = 0; while ((m = re.exec(fmt))) sum += (m[1] == undefined || m[1] == '' ? 1 : parseInt(m[1])) * magic[m[2]].length; return sum; }; /** * Pack a set of values, starting at offset, based on format * @internal */ const pack = function (fmt, values, offset = 0) { const littleendian = fmt.charAt(0) == '<'; offset = offset ? offset : 0; const ab = new ArrayBuffer(determineLength(fmt)), dv = new DataView(ab), re = new RegExp(pattern, 'g'); let m, c, l, i = 0; while ((m = re.exec(fmt))) { if (magic[m[2]] == undefined) throw new Error('Unknown format type'); c = m[1] == undefined || m[1] == '' ? 1 : parseInt(m[1]); l = magic[m[2]].length; if (offset + c * l > ab.byteLength) return null; const value = values.slice(i, i + 1); magic[m[2]].pack(dv, value, offset, c, littleendian); offset += c * l; i += 1; } return new Uint8Array(dv.buffer); }; /** * Unpack an arraybuffer, starting at offset, based on format * @internal */ const unpack = (fmt, ab, offset = 0) => { const littleendian = fmt.charAt(0) == '<'; const re = new RegExp(pattern, 'g'); let results = [], m, c, l; while ((m = re.exec(fmt))) { if (magic[m[2]] == undefined) throw new Error('Unknown format type'); c = m[1] == undefined || m[1] == '' ? 1 : parseInt(m[1]); l = magic[m[2]].length; if (offset + c * l > ab.byteLength) return null; results = results.concat(magic[m[2]].unpack(new DataView(ab), offset, c, littleendian)); offset += c * l; } return results; }; /** @internal */ function isAlphaNumeric(b) { if (b >= '0' && b <= '9') return true; if (b >= 'A' && b <= 'Z') return true; if (b >= 'a' && b <= 'z') return true; return false; } /** @internal */ function copyBuffer(buffer) { const dest = new ArrayBuffer(buffer.byteLength); const newBuffer = new Uint8Array(dest); newBuffer.set(buffer); return newBuffer; } /** @internal */ export { copyBuffer, determineLength, pack, unpack };