miijs 1.8.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FFLShaderMaterial.js +859 -0
- package/README.md +13 -9
- package/asmCrypto.js +3822 -10183
- package/crown.jpg +0 -0
- package/ffl-emscripten-single-file.js +21 -0
- package/ffl.js +5051 -0
- package/index.js +562 -394
- package/package.json +9 -6
- package/struct-fu.js +1140 -0
package/struct-fu.js
ADDED
|
@@ -0,0 +1,1140 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/*!
|
|
3
|
+
* struct-fu library: https://github.com/natevw/struct-fu
|
|
4
|
+
* forked by ariankordi: https://github.com/ariankordi/struct-fu
|
|
5
|
+
* @author Nathan Vander Wilt <https://github.com/natevw>
|
|
6
|
+
* @license Apache-2.0
|
|
7
|
+
* @license BSD-2-Clause
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// // ---------------------------------------------------------------------
|
|
11
|
+
// // Exported JSDoc Type Definitions
|
|
12
|
+
// // ---------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A type representing either a raw ArrayBuffer or a typed view of it.
|
|
16
|
+
* @typedef {ArrayBuffer | Uint8Array} BufferSource
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A type for the offset/"cursor" used to track the byte and bit offsets.
|
|
21
|
+
* @typedef {Object} Offset
|
|
22
|
+
* @property {number} bytes - Current byte offset.
|
|
23
|
+
* @property {number} [bits] - Current bit offset.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A transform interface for bitfields, converting between raw number bits and user values.
|
|
28
|
+
* Notably, the input/output can be number or boolean. (Used by `b2v` and `v2b` methods.)
|
|
29
|
+
* @typedef {Object} BitTransform
|
|
30
|
+
* @property {function(this: {width:number}, number): number|boolean} b2v - Called when reading bits from buffer.
|
|
31
|
+
* @property {function(this: {width:number}, number|boolean): number} v2b - Called when writing bits into buffer.
|
|
32
|
+
* @property {number} [width=1] - The width (in bits) of this bitfield.
|
|
33
|
+
* @property {boolean|null} [littleEndian] - Whether or not the bitfield is little-endian.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A transform interface for byte fields, converting between raw bytes and user values.
|
|
38
|
+
* (Used by `b2v` and `vTb` methods.)
|
|
39
|
+
* @typedef {Object} ByteTransform
|
|
40
|
+
* @property {function(BufferSource): *} b2v - Called when reading bytes from buffer.
|
|
41
|
+
* @property {function(*, Uint8Array): number} vTb - Called when writing values into buffer.
|
|
42
|
+
* @property {number} [size=1] - The size (in bytes) of this field.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Represents a padding field that ensures proper byte alignment in a struct.
|
|
47
|
+
* It does not hold a value but affects struct layout.
|
|
48
|
+
* @typedef {Object} PaddingField
|
|
49
|
+
* @property {number} _padTo - The byte offset to pad to.
|
|
50
|
+
* @property {string} [_id] - Internal unique ID.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A single field definition, which must define how to read (unpack) and write (pack) data.
|
|
55
|
+
* This is the base type that all specialized fields (bitfields, bytefields, etc.) implement.
|
|
56
|
+
* @typedef {Object} Field
|
|
57
|
+
* @property {string} [name] - The name of the field (if any).
|
|
58
|
+
* @property {number} size - The size of the field in bytes (for non-bitfield types).
|
|
59
|
+
* @property {number} [width] - The size of the field in bits (for bitfields).
|
|
60
|
+
* @property {number | Offset} [offset] - Byte or (byte, bits) offset.
|
|
61
|
+
* @property {function(BufferSource, Offset=): *} unpack - Unpacks the field value from a buffer (`valueFromBytes`).
|
|
62
|
+
* @property {function(*, BufferSource=, Offset=): Uint8Array} pack - Packs a value into a buffer (`bytesFromValue`).
|
|
63
|
+
* @property {Object<string, Field>|null} [_hoistFields] - If this is a nested struct, this maps sub-fields to their definitions.
|
|
64
|
+
* @property {Object<string, Field>} [fields] - An object mapping field names to their definitions.
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Template for the return type of _.struct(), representing an instance of a structure with pack/unpack methods.
|
|
69
|
+
* It is generic in case you want to define a typed object for the data.
|
|
70
|
+
* @template T
|
|
71
|
+
* @typedef StructInstance
|
|
72
|
+
* @property {function(BufferSource, Offset=): T} unpack - Deserialize from a BufferSource into the structured object.
|
|
73
|
+
* @property {function(T, BufferSource=, Offset=): Uint8Array} pack - Serialize the structured object into a Uint8Array.
|
|
74
|
+
* @property {Object<string, Field>} fields - Field definitions keyed by field name.
|
|
75
|
+
* @property {number} size - The total size in bytes of the packed structure.
|
|
76
|
+
* @property {string} [name] - The name of the struct (if provided).
|
|
77
|
+
* @property {Object<string, Field>|null} [_hoistFields] - If this is a nested struct, this maps sub-fields to their definitions.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
// // ---------------------------------------------------------------------
|
|
81
|
+
// // UMD / factory setup
|
|
82
|
+
// // ---------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
(function (root, factory) {
|
|
85
|
+
// @ts-ignore - cannot find name define
|
|
86
|
+
if (typeof define === 'function' && define.amd) {
|
|
87
|
+
// AMD. Register as an anonymous module.
|
|
88
|
+
// @ts-ignore
|
|
89
|
+
define([], factory);
|
|
90
|
+
} else if (typeof module === 'object' && module.exports) {
|
|
91
|
+
// Node.js/CommonJS
|
|
92
|
+
|
|
93
|
+
// In Node (in versions earlier than 11, released 2018), TextEn/Decoder
|
|
94
|
+
// are only available through the "util" package, however bundlers
|
|
95
|
+
// building for browser will see that and always try to include it.
|
|
96
|
+
|
|
97
|
+
// Therefore, this will assume TextEncoder/TextDecoder are in globalThis (not root).
|
|
98
|
+
module.exports = factory(globalThis.TextEncoder, globalThis.TextDecoder);
|
|
99
|
+
// NOTE: Uncomment below if you are using versions of Node earlier than 11.
|
|
100
|
+
// module.exports = factory(require('util').TextEncoder, require('util').TextDecoder);
|
|
101
|
+
} else {
|
|
102
|
+
// Browser globals (root is window)
|
|
103
|
+
|
|
104
|
+
// Assume TextEncoder/TextDecoder are either defined or undefined in window.
|
|
105
|
+
/** @type {*} */ (root)._ = factory(/** @type {*} */ (root).TextEncoder, /** @type {*} */ (root).TextDecoder);
|
|
106
|
+
}
|
|
107
|
+
}(typeof self !== 'undefined' ? self : this,
|
|
108
|
+
// It seems that TypeScript is predicting a type for the whole
|
|
109
|
+
// namespace that includes all functions, and defining any custom
|
|
110
|
+
// returns type ruins that, leaving the .d.ts empty and dry of functions.
|
|
111
|
+
/* eslint-disable jsdoc/require-returns-type */
|
|
112
|
+
/**
|
|
113
|
+
* @param {typeof TextEncoder} _TextEncoder
|
|
114
|
+
* @param {typeof TextDecoder} _TextDecoder
|
|
115
|
+
* @returns Returns the exported namespace.
|
|
116
|
+
*/
|
|
117
|
+
function (_TextEncoder, _TextDecoder) {
|
|
118
|
+
/* eslint-enable jsdoc/require-returns-type */
|
|
119
|
+
'use strict';
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A library for defining structs to convert between JSON and binary.
|
|
123
|
+
* Supports numbers, bytes, strings, and bitfields.
|
|
124
|
+
* Compatible with browsers down to Safari 5.1.
|
|
125
|
+
* @namespace _
|
|
126
|
+
*/
|
|
127
|
+
var _ = {};
|
|
128
|
+
|
|
129
|
+
// Add ECMA262-5 method binding if not supported natively
|
|
130
|
+
// https://github.com/ReactNativeNews/react-native-newsletter/blob/93016f62af32d97cc009f991d4f7c3c7155a4f26/ie.js#L8
|
|
131
|
+
if (!('bind' in Function.prototype)) {
|
|
132
|
+
/**
|
|
133
|
+
* @param {Object|null|undefined} owner
|
|
134
|
+
* @returns {Function}
|
|
135
|
+
* @this {Function}
|
|
136
|
+
*/
|
|
137
|
+
// @ts-ignore - Property bind does not exist on never?
|
|
138
|
+
Function.prototype.bind = function (owner) {
|
|
139
|
+
var that = this;
|
|
140
|
+
if (arguments.length <= 1) {
|
|
141
|
+
return function () {
|
|
142
|
+
return that.apply(owner, arguments);
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
var args = Array.prototype.slice.call(arguments, 1);
|
|
146
|
+
return function () {
|
|
147
|
+
return that.apply(
|
|
148
|
+
owner,
|
|
149
|
+
arguments.length === 0 ? args : args.concat(Array.prototype.slice.call(arguments))
|
|
150
|
+
);
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// // ---------------------------------------------------------------------
|
|
156
|
+
// // Helper Methods
|
|
157
|
+
// // ---------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Creates a new Uint8Array of given size backed by an ArrayBuffer.
|
|
161
|
+
* @param {number} size - The size of the buffer in bytes.
|
|
162
|
+
* @returns {Uint8Array} A new Uint8Array of the specified size.
|
|
163
|
+
*/
|
|
164
|
+
function newBuffer(size) {
|
|
165
|
+
return new Uint8Array(new ArrayBuffer(size));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Extends an object with properties from subsequent objects.
|
|
170
|
+
* @param {Object<string, *>} obj - The target object to extend.
|
|
171
|
+
* @returns {Object} The extended target object.
|
|
172
|
+
*/
|
|
173
|
+
function extend(obj) {
|
|
174
|
+
var args = /** @type {Array<Object<string, *>>} */ (Array.prototype.slice.call(arguments, 1));
|
|
175
|
+
args.forEach(function (ext) {
|
|
176
|
+
Object.keys(ext).forEach(function (key) {
|
|
177
|
+
obj[key] = ext[key];
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
return obj;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Adds a field's size to the current offset cursor (bytes/bits).
|
|
185
|
+
* @param {Offset} ctr - The current offset (bytes, bits).
|
|
186
|
+
* @param {Field} f - The field whose size to add.
|
|
187
|
+
* @returns {Offset} The updated offset cursor.
|
|
188
|
+
* @throws {Error} Improperly aligned bitfield before field
|
|
189
|
+
*/
|
|
190
|
+
function addField(ctr, f) {
|
|
191
|
+
if ('width' in f && typeof f.width === 'number') {
|
|
192
|
+
ctr.bits = (ctr.bits || 0) + f.width;
|
|
193
|
+
while ((ctr.bits || 0) > 7) {
|
|
194
|
+
ctr.bytes += 1;
|
|
195
|
+
ctr.bits -= 8;
|
|
196
|
+
}
|
|
197
|
+
} else if (!ctr.bits) {
|
|
198
|
+
// Not a bitfield, add full size in bytes.
|
|
199
|
+
ctr.bytes += f.size || 0;
|
|
200
|
+
} else {
|
|
201
|
+
// We have leftover bits that aren't aligned, can't add a normal field.
|
|
202
|
+
throw Error('Improperly aligned bitfield before field: ' + (f.name || ''));
|
|
203
|
+
}
|
|
204
|
+
return ctr;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Converts a field into an array field if a count is provided.
|
|
209
|
+
* @param {Field} f - The field to arrayize.
|
|
210
|
+
* @param {number} [count] - The number of elements in the array, if needed.
|
|
211
|
+
* @returns {Field} The arrayized field.
|
|
212
|
+
*/
|
|
213
|
+
function arrayizeField(f, count) {
|
|
214
|
+
var field = (typeof count === 'number') ? /** @type {Field} */ (extend({
|
|
215
|
+
name: f.name,
|
|
216
|
+
field: f,
|
|
217
|
+
/**
|
|
218
|
+
* Unpacks an array of values from bytes.
|
|
219
|
+
* @param {BufferSource} buf - The buffer to read from.
|
|
220
|
+
* @param {Offset} off - The offset object with bytes and bits.
|
|
221
|
+
* @returns {Array<*>} The unpacked array of values.
|
|
222
|
+
*/
|
|
223
|
+
unpack: function (buf, off) {
|
|
224
|
+
off || (off = { bytes: 0, bits: 0 });
|
|
225
|
+
var arr = new Array(count);
|
|
226
|
+
for (var idx = 0, len = arr.length; idx < len; idx += 1) {
|
|
227
|
+
arr[idx] = f.unpack(buf, off);
|
|
228
|
+
}
|
|
229
|
+
return arr;
|
|
230
|
+
},
|
|
231
|
+
/**
|
|
232
|
+
* Packs an array of values into bytes.
|
|
233
|
+
* @param {Array<*>} arr
|
|
234
|
+
* @param {BufferSource} [buf]
|
|
235
|
+
* @param {Offset} [off]
|
|
236
|
+
* @returns {Uint8Array}
|
|
237
|
+
* @this {Field}
|
|
238
|
+
*/
|
|
239
|
+
pack: function (arr, buf, off) {
|
|
240
|
+
arr || (arr = new Array(count));
|
|
241
|
+
buf || (buf = newBuffer(this.size));
|
|
242
|
+
off || (off = { bytes: 0, bits: 0 });
|
|
243
|
+
for (var idx = 0, len = Math.min(arr.length, count); idx < len; idx += 1) {
|
|
244
|
+
f.pack(arr[idx], buf, off);
|
|
245
|
+
}
|
|
246
|
+
while (idx++ < count) addField(off, f);
|
|
247
|
+
return /** @type {Uint8Array} */ (buf);
|
|
248
|
+
}
|
|
249
|
+
}, (f.width !== undefined)
|
|
250
|
+
? { width: f.width * count }
|
|
251
|
+
: { size: f.size * count })
|
|
252
|
+
) : f;
|
|
253
|
+
return field;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// // ---------------------------------------------------------------------
|
|
257
|
+
// // _.struct Definition
|
|
258
|
+
// // ---------------------------------------------------------------------
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Defines a new structure with the given fields.
|
|
262
|
+
* Overloads:
|
|
263
|
+
* _.struct(fields, count?)
|
|
264
|
+
* _.struct(name, fields, count?)
|
|
265
|
+
* @param {string|Array<Field>} name - The structure name or the array of fields if no name.
|
|
266
|
+
* @param {Array<Field|StructInstance<*>>|number} [fields] - The array of fields OR the count if the first param was fields.
|
|
267
|
+
* @param {number} [count] - The number of array elements if making an array of structs.
|
|
268
|
+
* @returns {StructInstance<*>} The resulting struct definition (and array, if count was provided).
|
|
269
|
+
* @throws {Error} Invalid .padTo..., Improperly aligned bitfield at end of struct
|
|
270
|
+
*/
|
|
271
|
+
_.struct = function (name, fields, count) {
|
|
272
|
+
/** @type {string|undefined} */
|
|
273
|
+
var structName;
|
|
274
|
+
/** @type {Array<Field>} */
|
|
275
|
+
var fieldDefs;
|
|
276
|
+
|
|
277
|
+
if (typeof name !== 'string') {
|
|
278
|
+
// Overload 1: (fields[, count])
|
|
279
|
+
count = /** @type {number|undefined} */ (fields);
|
|
280
|
+
fieldDefs = /** @type {Array<Field>} */ (name);
|
|
281
|
+
} else {
|
|
282
|
+
// Overload 2: (name, fields[, count])
|
|
283
|
+
structName = name;
|
|
284
|
+
fieldDefs = /** @type {Array<Field>} */ (fields);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
var _size = { bytes: 0, bits: 0 };
|
|
288
|
+
var _padsById = Object.create(null);
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* @param {Object<string, Field>} obj
|
|
292
|
+
* @param {Field} f
|
|
293
|
+
* @returns {Object<string, Field>}
|
|
294
|
+
* @throws {Error} Invalid .padTo...
|
|
295
|
+
*/
|
|
296
|
+
function reduceFunc(obj, f) {
|
|
297
|
+
// Handle _padTo:
|
|
298
|
+
if ('_padTo' in f) {
|
|
299
|
+
var padField = /** @type {PaddingField} */ (f); // Cast to this type.
|
|
300
|
+
// Generate a "pad field" for alignment.
|
|
301
|
+
if (!padField._id) {
|
|
302
|
+
padField._id = 'id' + Math.random().toFixed(20).slice(2); // WORKAROUND: https://github.com/tessel/runtime/issues/716
|
|
303
|
+
}
|
|
304
|
+
var neededPadBytes = padField._padTo - _size.bytes;
|
|
305
|
+
if (_size.bits) {
|
|
306
|
+
// This is a bitfield alignment.
|
|
307
|
+
var bitsNeeded = 8 * neededPadBytes - _size.bits;
|
|
308
|
+
if (bitsNeeded < 0) {
|
|
309
|
+
throw Error(
|
|
310
|
+
'Invalid .padTo(' + padField._padTo + ') field, struct is already ' +
|
|
311
|
+
_size.bytes + ' byte(s) and ' + _size.bits + ' bits!'
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
_padsById[padField._id] = { width: bitsNeeded };
|
|
315
|
+
f.width = bitsNeeded;
|
|
316
|
+
} else {
|
|
317
|
+
if (neededPadBytes < 0) {
|
|
318
|
+
throw Error(
|
|
319
|
+
'Invalid .padTo(' + padField._padTo + ') field, struct is already ' + _size.bytes + ' bytes!'
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
_padsById[padField._id] = { size: neededPadBytes };
|
|
323
|
+
f.size = neededPadBytes;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Handle sub-struct hoisting:
|
|
328
|
+
if (f._hoistFields) {
|
|
329
|
+
var hoistFields = f._hoistFields; // Otherwise it thinks it's null or undefined below
|
|
330
|
+
Object.keys(hoistFields).forEach(function (subName) {
|
|
331
|
+
var _f = Object.create(hoistFields[subName]);
|
|
332
|
+
if ('width' in _f && typeof _f.width === 'number') {
|
|
333
|
+
_f.offset = { bytes: _f.offset.bytes + _size.bytes, bits: _f.offset.bits };
|
|
334
|
+
} else {
|
|
335
|
+
_f.offset = /** @type {number} */ (_f.offset) + _size.bytes;
|
|
336
|
+
}
|
|
337
|
+
obj[subName] = _f;
|
|
338
|
+
});
|
|
339
|
+
} else if (f.name) {
|
|
340
|
+
// Create a local copy, assign offset.
|
|
341
|
+
var localCopy = Object.create(f);
|
|
342
|
+
if ('width' in localCopy && typeof localCopy.width === 'number') {
|
|
343
|
+
localCopy.offset = { bytes: _size.bytes, bits: _size.bits };
|
|
344
|
+
} else {
|
|
345
|
+
localCopy.offset = _size.bytes;
|
|
346
|
+
}
|
|
347
|
+
obj[f.name] = localCopy;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
addField(_size, f);
|
|
351
|
+
return obj;
|
|
352
|
+
}
|
|
353
|
+
/** @type {Object<string, Field>} */
|
|
354
|
+
var fieldsObj = fieldDefs.reduce(reduceFunc, {});
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
if (_size.bits) {
|
|
358
|
+
throw Error('Improperly aligned bitfield at end of struct: ' + (structName || ''));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Now build the main field definition for the struct.
|
|
362
|
+
var structField = {
|
|
363
|
+
/**
|
|
364
|
+
* Reads (unpacks) a struct from the buffer at the given offset.
|
|
365
|
+
* @param {BufferSource} buf
|
|
366
|
+
* @param {Offset} [off]
|
|
367
|
+
* @returns {*}
|
|
368
|
+
*/
|
|
369
|
+
unpack: function (buf, off) {
|
|
370
|
+
off = off || { bytes: 0, bits: 0 };
|
|
371
|
+
/** @type {Object<string, Field>} */
|
|
372
|
+
var obj = {};
|
|
373
|
+
fieldDefs.forEach(function (f) {
|
|
374
|
+
if ('_padTo' in f && /** @type {PaddingField} */ (f)._id !== undefined) {
|
|
375
|
+
// It's a pad spec; retrieve the pad field from _padsById.
|
|
376
|
+
addField(/** @type {Offset} */ (off), _padsById[/** @type {PaddingField} */ (f)._id]);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
var value = f.unpack(buf, off);
|
|
380
|
+
if (f.name) {
|
|
381
|
+
obj[f.name] = value;
|
|
382
|
+
} else if (typeof value === 'object') {
|
|
383
|
+
extend(obj, value);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
return obj;
|
|
387
|
+
},
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Writes (packs) the given object into a buffer at the given offset.
|
|
391
|
+
* @param {*} obj
|
|
392
|
+
* @param {BufferSource} [buf]
|
|
393
|
+
* @param {Offset} [off]
|
|
394
|
+
* @returns {Uint8Array}
|
|
395
|
+
*/
|
|
396
|
+
pack: function (obj, buf, off) {
|
|
397
|
+
obj = obj || {};
|
|
398
|
+
if (!buf) {
|
|
399
|
+
buf = newBuffer(this.size);
|
|
400
|
+
}
|
|
401
|
+
off = off || { bytes: 0, bits: 0 };
|
|
402
|
+
fieldDefs.forEach(function (f) {
|
|
403
|
+
if ('_padTo' in f && /** @type {PaddingField} */ (f)._id !== undefined) {
|
|
404
|
+
addField(/** @type {Offset} */ (off), _padsById[/** @type {PaddingField} */ (f)._id]);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
var value = f.name ? obj[f.name] : obj;
|
|
408
|
+
f.pack(value, /** @type {BufferSource} */ (buf), off);
|
|
409
|
+
});
|
|
410
|
+
return /** @type {Uint8Array} */ (buf);
|
|
411
|
+
},
|
|
412
|
+
|
|
413
|
+
_hoistFields: structName ? null : fieldsObj,
|
|
414
|
+
fields: fieldsObj,
|
|
415
|
+
size: _size.bytes,
|
|
416
|
+
name: structName
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// Cast to StructInstance for type compatibility.
|
|
420
|
+
return /** @type {StructInstance<*>} */ (arrayizeField(structField, count));
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
// // ---------------------------------------------------------------------
|
|
424
|
+
// // Begin Bitfield Helpers
|
|
425
|
+
// // ---------------------------------------------------------------------
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Reads a 32-bit unsigned int from buffer, but handles short lengths by not reading beyond the buffer.
|
|
429
|
+
* Used in valueFromBytes by bitfield logic.
|
|
430
|
+
* @param {BufferSource} buffer - The buffer to read from.
|
|
431
|
+
* @param {number} offset - The byte offset to start reading.
|
|
432
|
+
* @param {boolean} littleEndian - Indicates whether to read little-endian.
|
|
433
|
+
* @returns {number} The read unsigned 32-bit integer.
|
|
434
|
+
*/
|
|
435
|
+
function truncatedReadUInt32(buffer, offset, littleEndian) {
|
|
436
|
+
var bytes = (buffer instanceof ArrayBuffer) ? new Uint8Array(buffer) : buffer;
|
|
437
|
+
var availableBytes = bytes.length - offset;
|
|
438
|
+
var view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
439
|
+
|
|
440
|
+
if (availableBytes >= 4) {
|
|
441
|
+
return view.getUint32(offset, littleEndian);
|
|
442
|
+
} else if (availableBytes === 3) {
|
|
443
|
+
var first = view.getUint16(offset, littleEndian);
|
|
444
|
+
var second = view.getUint8(offset + 2);
|
|
445
|
+
return littleEndian
|
|
446
|
+
? ((second << 16) + first) >>> 0
|
|
447
|
+
: ((first << 8) + second) << 8 >>> 0;
|
|
448
|
+
} else if (availableBytes === 2) {
|
|
449
|
+
return view.getUint16(offset, littleEndian) << (littleEndian ? 0 : 16) >>> 0;
|
|
450
|
+
} else if (availableBytes === 1) {
|
|
451
|
+
return view.getUint8(offset) << (littleEndian ? 0 : 24) >>> 0;
|
|
452
|
+
}
|
|
453
|
+
return 0x0;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Writes a 32-bit unsigned int to buffer, but handles short lengths by not writing beyond the buffer.
|
|
458
|
+
* Used in bytesFromValue/pack by bitfield logic.
|
|
459
|
+
* @param {BufferSource} buffer - The buffer to write to.
|
|
460
|
+
* @param {number} offset - The byte offset to start writing.
|
|
461
|
+
* @param {number} data - The unsigned 32-bit integer to write.
|
|
462
|
+
* @param {boolean} littleEndian - Indicates whether to write little-endian.
|
|
463
|
+
*/
|
|
464
|
+
function truncatedWriteUInt32(buffer, offset, data, littleEndian) {
|
|
465
|
+
var bytes = (buffer instanceof ArrayBuffer) ? new Uint8Array(buffer) : buffer;
|
|
466
|
+
var availableBytes = bytes.length - offset;
|
|
467
|
+
var view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
468
|
+
|
|
469
|
+
if (availableBytes >= 4) {
|
|
470
|
+
view.setUint32(offset, data, littleEndian);
|
|
471
|
+
} else if (availableBytes === 3) {
|
|
472
|
+
if (littleEndian) {
|
|
473
|
+
view.setUint8(offset, data & 0xff);
|
|
474
|
+
view.setUint16(offset + 1, data >>> 8, littleEndian);
|
|
475
|
+
} else {
|
|
476
|
+
view.setUint16(offset, data >>> 16, littleEndian);
|
|
477
|
+
view.setUint8(offset + 2, (data >>> 8) & 0xff);
|
|
478
|
+
}
|
|
479
|
+
} else if (availableBytes === 2) {
|
|
480
|
+
view.setUint16(offset, littleEndian ? data & 0xffff : data >>> 16, littleEndian);
|
|
481
|
+
} else if (availableBytes === 1) {
|
|
482
|
+
view.setUint8(offset, littleEndian ? data & 0xff : data >>> 24);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Defines a padding field up to the specified offset in bytes.
|
|
488
|
+
* @param {number} off - The byte offset to pad to.
|
|
489
|
+
* @returns {Field & {_padTo: number, _id?: string}} The padding field definition.
|
|
490
|
+
*/
|
|
491
|
+
_.padTo = function (off) {
|
|
492
|
+
var field = /** @type {Field & {_padTo: number}} */ ({ _padTo: off,
|
|
493
|
+
// Dummy implementations to satisfy Field:
|
|
494
|
+
size: 0,
|
|
495
|
+
unpack: function () { return null; },
|
|
496
|
+
pack: function () { return newBuffer(0); }
|
|
497
|
+
});
|
|
498
|
+
return field;
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
// NOTE: bitfields must be embedded in a struct (C/C++ share this limitation)
|
|
503
|
+
|
|
504
|
+
var FULL = 0xFFFFFFFF;
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* A helper for big and little endian bitfields.
|
|
508
|
+
* @param {string} [name] - The name of the bitfield.
|
|
509
|
+
* @param {number} [width=1] - The width of the bitfield in bits.
|
|
510
|
+
* @param {number} [count] - The number of bitfields in an array.
|
|
511
|
+
* @returns {Field} The defined bitfield.
|
|
512
|
+
* @throws {Error} Bitfields support a maximum width of 24 bits
|
|
513
|
+
* @this {BitTransform}
|
|
514
|
+
*/
|
|
515
|
+
function bitfield(name, width, count) {
|
|
516
|
+
if (width === undefined) {
|
|
517
|
+
width = 1;
|
|
518
|
+
}
|
|
519
|
+
// NOTE: width limitation is so all values will align *within* a 4-byte word
|
|
520
|
+
if (width > 24) {
|
|
521
|
+
throw Error('Bitfields support a maximum width of 24 bits.');
|
|
522
|
+
}
|
|
523
|
+
var mask = FULL >>> (32 - width);
|
|
524
|
+
var littleEndian = this.littleEndian;
|
|
525
|
+
if (littleEndian) {
|
|
526
|
+
mask >>>= 0;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
var impl = this;
|
|
530
|
+
return arrayizeField({
|
|
531
|
+
/**
|
|
532
|
+
* @param {BufferSource} buf
|
|
533
|
+
* @param {Offset} [off]
|
|
534
|
+
* @returns {number|boolean}
|
|
535
|
+
* @this {Field & {width: number}}
|
|
536
|
+
*/
|
|
537
|
+
unpack: function (buf, off) {
|
|
538
|
+
off = off || { bytes: 0, bits: 0 };
|
|
539
|
+
var over;
|
|
540
|
+
var end;
|
|
541
|
+
var word;
|
|
542
|
+
if (littleEndian) {
|
|
543
|
+
end = off.bits || 0;
|
|
544
|
+
word = truncatedReadUInt32(buf, off.bytes, true) >>> 0;
|
|
545
|
+
over = (word >>> end);
|
|
546
|
+
} else {
|
|
547
|
+
end = (off.bits || 0) + /** @type {number} */ (width);
|
|
548
|
+
word = truncatedReadUInt32(buf, off.bytes, false) || 0;
|
|
549
|
+
over = word >>> (32 - end);
|
|
550
|
+
}
|
|
551
|
+
addField(off, this);
|
|
552
|
+
return impl.b2v.call(this, over & mask);
|
|
553
|
+
},
|
|
554
|
+
/**
|
|
555
|
+
* @param {number} val
|
|
556
|
+
* @param {BufferSource} [buf]
|
|
557
|
+
* @param {Offset} [off]
|
|
558
|
+
* @returns {Uint8Array}
|
|
559
|
+
* @this {Field & {width: number}}
|
|
560
|
+
*/
|
|
561
|
+
pack: function (val, buf, off) {
|
|
562
|
+
val = impl.v2b.call(this, val || 0);
|
|
563
|
+
if (!buf) {
|
|
564
|
+
buf = newBuffer(/** @type {number} */(this.size));
|
|
565
|
+
}
|
|
566
|
+
off = off || { bytes: 0, bits: 0 };
|
|
567
|
+
var word;
|
|
568
|
+
var zero;
|
|
569
|
+
var over;
|
|
570
|
+
if (littleEndian) {
|
|
571
|
+
word = truncatedReadUInt32(buf, off.bytes, true) >>> 0;
|
|
572
|
+
var shift = off.bits || 0;
|
|
573
|
+
zero = (mask << shift) >>> 0;
|
|
574
|
+
word &= ~zero;
|
|
575
|
+
over = (val & mask) << shift;
|
|
576
|
+
word = (word | over) >>> 0; // WORKAROUND: https://github.com/tessel/runtime/issues/644
|
|
577
|
+
truncatedWriteUInt32(buf, off.bytes, word, true);
|
|
578
|
+
} else {
|
|
579
|
+
var end = (off.bits || 0) + /** @type {number} */ (width);
|
|
580
|
+
word = truncatedReadUInt32(buf, off.bytes, false) || 0;
|
|
581
|
+
zero = mask << (32 - end);
|
|
582
|
+
over = (val & mask) << (32 - end);
|
|
583
|
+
word &= ~zero;
|
|
584
|
+
word = (word | over) >>> 0; // WORKAROUND: https://github.com/tessel/runtime/issues/644
|
|
585
|
+
truncatedWriteUInt32(buf, off.bytes, word, false);
|
|
586
|
+
}
|
|
587
|
+
addField(off, this);
|
|
588
|
+
return new Uint8Array(buf);
|
|
589
|
+
},
|
|
590
|
+
width: width,
|
|
591
|
+
size: 0, // Placeholder for bitfields.
|
|
592
|
+
name: name
|
|
593
|
+
}, count);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// // ---------------------------------------------------------------------
|
|
597
|
+
// // Begin Bitfield Definitions
|
|
598
|
+
// // ---------------------------------------------------------------------
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Boolean field: 1-bit big-endian bitfield that interprets 1/0 as true/false.
|
|
602
|
+
* @param {string} [name]
|
|
603
|
+
* @param {number} [count]
|
|
604
|
+
* @returns {Field}
|
|
605
|
+
*/
|
|
606
|
+
_.bool = function (name, count) {
|
|
607
|
+
return bitfield.call({
|
|
608
|
+
/**
|
|
609
|
+
* Converts a bitfield to a boolean.
|
|
610
|
+
* @param {number} b - The bitfield value.
|
|
611
|
+
* @returns {boolean} The boolean representation.
|
|
612
|
+
*/
|
|
613
|
+
b2v: function (b) { return Boolean(b); },
|
|
614
|
+
/**
|
|
615
|
+
* Converts a boolean to a bitfield.
|
|
616
|
+
* @param {number|boolean} v - The boolean value.
|
|
617
|
+
* @returns {number} The bitfield representation.
|
|
618
|
+
*/
|
|
619
|
+
v2b: function (v) { return v ? FULL : 0; }
|
|
620
|
+
}, name, 1, count);
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Unsigned bitfield (big-endian).
|
|
625
|
+
*/
|
|
626
|
+
_.ubit = bitfield.bind({
|
|
627
|
+
/**
|
|
628
|
+
* Converts a bitfield to a value.
|
|
629
|
+
* @param {number} b - The bitfield value.
|
|
630
|
+
* @returns {number} The numeric value.
|
|
631
|
+
*/
|
|
632
|
+
b2v: function (b) { return b; },
|
|
633
|
+
/**
|
|
634
|
+
* Converts a value to a bitfield.
|
|
635
|
+
* @param {number|boolean} v - The numeric value.
|
|
636
|
+
* @returns {number} The bitfield representation.
|
|
637
|
+
*/
|
|
638
|
+
v2b: function (v) { return Number(v); }
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Unsigned bitfield (little-endian).
|
|
643
|
+
* @param {string} name - The name of the bitfield.
|
|
644
|
+
* @param {number} [width=1] - The width of the bitfield in bits.
|
|
645
|
+
* @param {number} [count] - The number of bitfields in an array.
|
|
646
|
+
* @returns {Object} The defined little-endian bitfield.
|
|
647
|
+
*/
|
|
648
|
+
_.ubitLE = bitfield.bind({
|
|
649
|
+
/**
|
|
650
|
+
* Converts a bitfield to a little-endian value.
|
|
651
|
+
* @param {number} b - The bitfield value.
|
|
652
|
+
* @returns {number} The little-endian numeric value.
|
|
653
|
+
*/
|
|
654
|
+
b2v: function (b) { return b; },
|
|
655
|
+
/**
|
|
656
|
+
* Converts a little-endian value to a bitfield.
|
|
657
|
+
* @param {number|boolean} v - The little-endian numeric value.
|
|
658
|
+
* @returns {number} The bitfield representation.
|
|
659
|
+
*/
|
|
660
|
+
v2b: function (v) { return Number(v); },
|
|
661
|
+
littleEndian: true
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Signed bitfield (big-endian).
|
|
666
|
+
* @param {string} name - The name of the bitfield.
|
|
667
|
+
* @param {number} [width=1] - The width of the bitfield in bits.
|
|
668
|
+
* @param {number} [count] - The number of bitfields in an array.
|
|
669
|
+
* @returns {Object} The defined signed bitfield.
|
|
670
|
+
*/
|
|
671
|
+
_.sbit = bitfield.bind({
|
|
672
|
+
/**
|
|
673
|
+
* Converts a bitfield to a signed value.
|
|
674
|
+
* @param {number} b - The bitfield value.
|
|
675
|
+
* @returns {number} The signed numeric value.
|
|
676
|
+
*/
|
|
677
|
+
b2v: function (b) {
|
|
678
|
+
var m = 1 << ((this.width || 1) - 1);
|
|
679
|
+
var s = b & m;
|
|
680
|
+
return (s) ? -(b &= ~m) : b;
|
|
681
|
+
},
|
|
682
|
+
/**
|
|
683
|
+
* Converts a signed value to a bitfield.
|
|
684
|
+
* @param {number|boolean} v - The signed numeric value.
|
|
685
|
+
* @returns {number} The bitfield representation.
|
|
686
|
+
*/
|
|
687
|
+
v2b: function (v) {
|
|
688
|
+
v = Number(v);
|
|
689
|
+
var m = 1 << ((this.width || 1) - 1);
|
|
690
|
+
var s = (v < 0);
|
|
691
|
+
return (s) ? (-v | m) : v;
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Creates a simple "byte field" for reading/writing raw bytes.
|
|
697
|
+
* @param {string|number} name - The name of the bytefield or its size if name is omitted.
|
|
698
|
+
* @param {number} [size=1] - The size of the bytefield in bytes.
|
|
699
|
+
* @param {number} [count] - The number of bytefields in an array.
|
|
700
|
+
* @returns {Field & ByteTransform} The defined bytefield.
|
|
701
|
+
* @this {ByteTransform}
|
|
702
|
+
*/
|
|
703
|
+
function bytefield(name, size, count) {
|
|
704
|
+
/** @type {string|undefined} */
|
|
705
|
+
var fieldName;
|
|
706
|
+
/** @type {number} */
|
|
707
|
+
var fieldSize = 1;
|
|
708
|
+
if (typeof name === 'string') {
|
|
709
|
+
fieldName = name;
|
|
710
|
+
if (typeof size === 'number') {
|
|
711
|
+
fieldSize = size;
|
|
712
|
+
}
|
|
713
|
+
} else {
|
|
714
|
+
// Shift params.
|
|
715
|
+
fieldSize = name;
|
|
716
|
+
count = size; // pass along
|
|
717
|
+
}
|
|
718
|
+
var impl = this;
|
|
719
|
+
|
|
720
|
+
return /** @type {Field & ByteTransform} */ (arrayizeField(/** @type {Field & ByteTransform} */ ({
|
|
721
|
+
name: fieldName,
|
|
722
|
+
size: fieldSize,
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* @param {BufferSource} buf
|
|
726
|
+
* @param {Offset} [off]
|
|
727
|
+
* @returns {Uint8Array}
|
|
728
|
+
*/
|
|
729
|
+
unpack: function (buf, off) {
|
|
730
|
+
off = off || { bytes: 0, bits: 0 };
|
|
731
|
+
var bytes = (buf instanceof ArrayBuffer) ? new Uint8Array(buf) : buf;
|
|
732
|
+
var val = bytes.subarray(off.bytes, off.bytes + /** @type {number} */ (this.size));
|
|
733
|
+
addField(off, this);
|
|
734
|
+
return impl.b2v.call(this, val);
|
|
735
|
+
},
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* @param {Array<number>|Uint8Array|ArrayBuffer} val
|
|
739
|
+
* @param {BufferSource} [buf]
|
|
740
|
+
* @param {Offset} [off]
|
|
741
|
+
* @returns {Uint8Array}
|
|
742
|
+
*/
|
|
743
|
+
pack: function (val, buf, off) {
|
|
744
|
+
if (!buf) {
|
|
745
|
+
buf = newBuffer(/** @type {number} */ (this.size));
|
|
746
|
+
}
|
|
747
|
+
off = off || { bytes: 0, bits: 0 };
|
|
748
|
+
var bytes = (buf instanceof ArrayBuffer) ? new Uint8Array(buf) : buf;
|
|
749
|
+
var blk = bytes.subarray(off.bytes, off.bytes + /** @type {number} */ (this.size));
|
|
750
|
+
impl.vTb.call(this, val, blk);
|
|
751
|
+
addField(off, this);
|
|
752
|
+
return /** @type {Uint8Array} */ (buf);
|
|
753
|
+
},
|
|
754
|
+
|
|
755
|
+
// Default transforms:
|
|
756
|
+
/**
|
|
757
|
+
* @param {Uint8Array} b
|
|
758
|
+
* @returns {Uint8Array}
|
|
759
|
+
*/
|
|
760
|
+
b2v: function (b) { return b; },
|
|
761
|
+
/**
|
|
762
|
+
* @param {Array<number>|Uint8Array|ArrayBuffer} v
|
|
763
|
+
* @param {Uint8Array} b
|
|
764
|
+
* @returns {number}
|
|
765
|
+
*/
|
|
766
|
+
vTb: function (v, b) {
|
|
767
|
+
if (!v) return 0;
|
|
768
|
+
var src = new Uint8Array(v);
|
|
769
|
+
b.set(src.subarray(0, b.length));
|
|
770
|
+
return b.length;
|
|
771
|
+
}
|
|
772
|
+
}), count));
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Swaps adjacent byte pairs in a buffer, used for big-endian <-> little-endian char16 manipulations.
|
|
777
|
+
* http://stackoverflow.com/a/7460958/72637
|
|
778
|
+
* @param {Uint8Array} fromBuffer - The source buffer.
|
|
779
|
+
* @param {Uint8Array} [toBuffer] - The destination buffer. If not provided, fromBuffer is modified.
|
|
780
|
+
* @returns {Uint8Array} The buffer with swapped byte pairs.
|
|
781
|
+
*/
|
|
782
|
+
function swapBytesPairs(fromBuffer, toBuffer) {
|
|
783
|
+
toBuffer = toBuffer || fromBuffer;
|
|
784
|
+
var l = fromBuffer.length;
|
|
785
|
+
for (var i = 1; i < l; i += 2) {
|
|
786
|
+
var a = fromBuffer[i - 1];
|
|
787
|
+
toBuffer[i - 1] = fromBuffer[i];
|
|
788
|
+
toBuffer[i] = a;
|
|
789
|
+
}
|
|
790
|
+
return toBuffer;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/** Basic raw byte field. */
|
|
794
|
+
_.byte = bytefield.bind({
|
|
795
|
+
/**
|
|
796
|
+
* Converts bytes to a value.
|
|
797
|
+
* @param {BufferSource} b - The bytes to convert.
|
|
798
|
+
* @returns {Uint8Array} The byte value.
|
|
799
|
+
*/
|
|
800
|
+
b2v: function (b) { return new Uint8Array(b); },
|
|
801
|
+
/**
|
|
802
|
+
* Converts a value to bytes.
|
|
803
|
+
* @param {BufferSource} v - The value to convert.
|
|
804
|
+
* @param {Uint8Array} b - The buffer to write to.
|
|
805
|
+
* @returns {number} The number of bytes written.
|
|
806
|
+
*/
|
|
807
|
+
vTb: function (v, b) { if (!v) return 0; b.set(new Uint8Array(v)); return v.byteLength; }
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
// // ---------------------------------------------------------------------
|
|
811
|
+
// // Character Field Definitions
|
|
812
|
+
// // ---------------------------------------------------------------------
|
|
813
|
+
|
|
814
|
+
/** Null-terminated UTF-8 string field. */
|
|
815
|
+
_.char = bytefield.bind({
|
|
816
|
+
/**
|
|
817
|
+
* Converts bytes to a UTF-8 string.
|
|
818
|
+
* @param {BufferSource} b - The bytes to convert.
|
|
819
|
+
* @returns {string} The resulting string.
|
|
820
|
+
*/
|
|
821
|
+
b2v: function (b) {
|
|
822
|
+
var decoder;
|
|
823
|
+
if (typeof _TextDecoder !== 'undefined' && _TextDecoder) {
|
|
824
|
+
decoder = new _TextDecoder('utf-8');
|
|
825
|
+
} else {
|
|
826
|
+
// Fallback minimal polyfill
|
|
827
|
+
decoder = {
|
|
828
|
+
/**
|
|
829
|
+
* @param {BufferSource} buf
|
|
830
|
+
* @returns {string}
|
|
831
|
+
*/
|
|
832
|
+
decode: function (buf) {
|
|
833
|
+
var bytes = new Uint8Array(buf);
|
|
834
|
+
var str = '';
|
|
835
|
+
for (var i = 0; i < bytes.length; i++) {
|
|
836
|
+
str += String.fromCharCode(bytes[i]);
|
|
837
|
+
}
|
|
838
|
+
return str;
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
var v = decoder.decode(b);
|
|
843
|
+
var z = v.indexOf('\0');
|
|
844
|
+
return (~z) ? v.slice(0, z) : v;
|
|
845
|
+
},
|
|
846
|
+
/**
|
|
847
|
+
* Converts a string to UTF-8 bytes.
|
|
848
|
+
* @param {string} v - The string to convert.
|
|
849
|
+
* @param {Uint8Array} b - The buffer to write to.
|
|
850
|
+
* @returns {number} The number of bytes written.
|
|
851
|
+
*/
|
|
852
|
+
vTb: function (v, b) {
|
|
853
|
+
v || (v = '');
|
|
854
|
+
var encoder;
|
|
855
|
+
if (typeof _TextEncoder !== 'undefined' && _TextEncoder) {
|
|
856
|
+
encoder = new _TextEncoder();
|
|
857
|
+
} else {
|
|
858
|
+
// Fallback minimal polyfill
|
|
859
|
+
encoder = {
|
|
860
|
+
/**
|
|
861
|
+
* @param {string} str
|
|
862
|
+
* @returns {Uint8Array}
|
|
863
|
+
*/
|
|
864
|
+
encode: function (str) {
|
|
865
|
+
var arr = new Uint8Array(str.length);
|
|
866
|
+
for (var i = 0; i < str.length; i++) {
|
|
867
|
+
arr[i] = str.charCodeAt(i) & 0xff;
|
|
868
|
+
}
|
|
869
|
+
return arr;
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
var encoded = encoder.encode(v);
|
|
874
|
+
for (var i = 0; i < encoded.length && i < b.length; i++) {
|
|
875
|
+
b[i] = encoded[i];
|
|
876
|
+
}
|
|
877
|
+
return encoded.length;
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
/** Null-terminated UTF-16LE string field. */
|
|
882
|
+
_.char16le = bytefield.bind({
|
|
883
|
+
/**
|
|
884
|
+
* Converts bytes to a UTF-16LE string.
|
|
885
|
+
* @param {BufferSource} b - The bytes to convert.
|
|
886
|
+
* @returns {string} The resulting string.
|
|
887
|
+
*/
|
|
888
|
+
b2v: function (b) {
|
|
889
|
+
var decoder;
|
|
890
|
+
if (typeof _TextDecoder !== 'undefined' && _TextDecoder) {
|
|
891
|
+
decoder = new _TextDecoder('utf-16le');
|
|
892
|
+
} else {
|
|
893
|
+
// fallback
|
|
894
|
+
decoder = {
|
|
895
|
+
/**
|
|
896
|
+
* @param {BufferSource} buf
|
|
897
|
+
* @returns {string}
|
|
898
|
+
*/
|
|
899
|
+
decode: function (buf) {
|
|
900
|
+
var bytes = new Uint8Array(buf);
|
|
901
|
+
var str = '';
|
|
902
|
+
for (var i = 0; i < bytes.length; i += 2) {
|
|
903
|
+
var charCode = bytes[i] | (bytes[i + 1] << 8);
|
|
904
|
+
str += String.fromCharCode(charCode);
|
|
905
|
+
}
|
|
906
|
+
return str;
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
var v = decoder.decode(b);
|
|
911
|
+
var z = v.indexOf('\0');
|
|
912
|
+
return (~z) ? v.slice(0, z) : v;
|
|
913
|
+
},
|
|
914
|
+
/**
|
|
915
|
+
* Converts a string to UTF-16LE bytes.
|
|
916
|
+
* @param {string} v - The string to convert.
|
|
917
|
+
* @param {Uint8Array} b - The buffer to write to.
|
|
918
|
+
* @returns {number} The number of bytes written.
|
|
919
|
+
*/
|
|
920
|
+
vTb: function (v, b) {
|
|
921
|
+
v || (v = '');
|
|
922
|
+
var bytesWritten = 0;
|
|
923
|
+
for (var i = 0; i < v.length && bytesWritten + 1 < b.length; i++) {
|
|
924
|
+
var charCode = v.charCodeAt(i);
|
|
925
|
+
b[bytesWritten++] = charCode & 0xFF;
|
|
926
|
+
b[bytesWritten++] = (charCode >> 8) & 0xFF;
|
|
927
|
+
}
|
|
928
|
+
return bytesWritten;
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
/** Null-terminated UTF-16BE string field. */
|
|
933
|
+
_.char16be = bytefield.bind({
|
|
934
|
+
/**
|
|
935
|
+
* Converts bytes to a UTF-16BE string.
|
|
936
|
+
* @param {BufferSource} b - The bytes to convert.
|
|
937
|
+
* @returns {string} The resulting string.
|
|
938
|
+
*/
|
|
939
|
+
b2v: function (b) {
|
|
940
|
+
var temp = new Uint8Array(b);
|
|
941
|
+
swapBytesPairs(temp);
|
|
942
|
+
var decoder;
|
|
943
|
+
if (typeof _TextDecoder !== 'undefined' && _TextDecoder) {
|
|
944
|
+
decoder = new _TextDecoder('utf-16le');
|
|
945
|
+
} else {
|
|
946
|
+
// fallback
|
|
947
|
+
decoder = {
|
|
948
|
+
/**
|
|
949
|
+
* @param {BufferSource} buf
|
|
950
|
+
* @returns {string}
|
|
951
|
+
*/
|
|
952
|
+
decode: function (buf) {
|
|
953
|
+
var bytes = new Uint8Array(buf);
|
|
954
|
+
var str = '';
|
|
955
|
+
for (var i = 0; i < bytes.length; i += 2) {
|
|
956
|
+
var charCode = bytes[i] | (bytes[i + 1] << 8);
|
|
957
|
+
str += String.fromCharCode(charCode);
|
|
958
|
+
}
|
|
959
|
+
return str;
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
var v = decoder.decode(/** @type {ArrayBuffer} */ (temp.buffer));
|
|
964
|
+
var z = v.indexOf('\0');
|
|
965
|
+
return (~z) ? v.slice(0, z) : v;
|
|
966
|
+
},
|
|
967
|
+
/**
|
|
968
|
+
* Converts a string to UTF-16BE bytes.
|
|
969
|
+
* @param {string} v - The string to convert.
|
|
970
|
+
* @param {Uint8Array} b - The buffer to write to.
|
|
971
|
+
* @returns {number} The number of bytes written.
|
|
972
|
+
*/
|
|
973
|
+
vTb: function (v, b) {
|
|
974
|
+
v || (v = '');
|
|
975
|
+
var temp = new Uint8Array(b.length);
|
|
976
|
+
var bytesWritten = 0;
|
|
977
|
+
for (var i = 0; i < v.length && bytesWritten + 1 < temp.length; i++) {
|
|
978
|
+
var charCode = v.charCodeAt(i);
|
|
979
|
+
temp[bytesWritten++] = charCode & 0xFF;
|
|
980
|
+
temp[bytesWritten++] = (charCode >> 8) & 0xFF;
|
|
981
|
+
}
|
|
982
|
+
swapBytesPairs(temp, b);
|
|
983
|
+
return bytesWritten;
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
// // ---------------------------------------------------------------------
|
|
988
|
+
// // dataViewField Definition
|
|
989
|
+
// // ---------------------------------------------------------------------
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* Defines a standard field based on read/write methods for a DataView.
|
|
993
|
+
* @param {function(this:DataView, number, boolean):number} getFn - The DataView getter.
|
|
994
|
+
* @param {function(this:DataView, number, number, boolean):void} setFn - The DataView setter.
|
|
995
|
+
* @param {number} size - The size of the field in bytes (4 for Uint32, etc.).
|
|
996
|
+
* @param {boolean} littleEndian - Indicates whether or not the field is little endian.
|
|
997
|
+
* @returns {function((string|number), number=): Field} A function to create the field.
|
|
998
|
+
*/
|
|
999
|
+
function dataViewField(getFn, setFn, size, littleEndian) {
|
|
1000
|
+
/**
|
|
1001
|
+
* @param {string|number} name
|
|
1002
|
+
* @param {number} [count]
|
|
1003
|
+
* @returns {Field}
|
|
1004
|
+
*/
|
|
1005
|
+
var func = function (name, count) {
|
|
1006
|
+
/** @type {string|undefined} */
|
|
1007
|
+
var fieldName;
|
|
1008
|
+
if (typeof name === 'string') {
|
|
1009
|
+
fieldName = name;
|
|
1010
|
+
} else {
|
|
1011
|
+
// shift
|
|
1012
|
+
count = name;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
return arrayizeField({
|
|
1016
|
+
name: fieldName,
|
|
1017
|
+
size: size,
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* @param {BufferSource} buf
|
|
1021
|
+
* @param {Offset} [off]
|
|
1022
|
+
* @returns {number}
|
|
1023
|
+
*/
|
|
1024
|
+
unpack: function (buf, off) {
|
|
1025
|
+
off = off || { bytes: 0 };
|
|
1026
|
+
var bytes = (buf instanceof ArrayBuffer) ? new Uint8Array(buf) : buf;
|
|
1027
|
+
var view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
1028
|
+
var val = getFn.call(view, off.bytes, littleEndian);
|
|
1029
|
+
addField(off, this);
|
|
1030
|
+
return val;
|
|
1031
|
+
},
|
|
1032
|
+
/**
|
|
1033
|
+
* @param {number} val
|
|
1034
|
+
* @param {BufferSource} [buf]
|
|
1035
|
+
* @param {Offset} [off]
|
|
1036
|
+
* @returns {Uint8Array}
|
|
1037
|
+
*/
|
|
1038
|
+
pack: function (val, buf, off) {
|
|
1039
|
+
if (val === undefined || val === null) {
|
|
1040
|
+
val = 0;
|
|
1041
|
+
}
|
|
1042
|
+
if (!buf) {
|
|
1043
|
+
buf = newBuffer(this.size);
|
|
1044
|
+
}
|
|
1045
|
+
off = off || { bytes: 0 };
|
|
1046
|
+
var bytes = (buf instanceof ArrayBuffer) ? new Uint8Array(buf) : buf;
|
|
1047
|
+
var view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
1048
|
+
setFn.call(view, off.bytes, val, littleEndian);
|
|
1049
|
+
addField(off, this);
|
|
1050
|
+
return new Uint8Array(buf);
|
|
1051
|
+
}
|
|
1052
|
+
}, count);
|
|
1053
|
+
};
|
|
1054
|
+
return func;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// // ---------------------------------------------------------------------
|
|
1058
|
+
// // DataView Field Definitions
|
|
1059
|
+
// // ---------------------------------------------------------------------
|
|
1060
|
+
|
|
1061
|
+
var DV = DataView.prototype; // Alias for brevity.
|
|
1062
|
+
|
|
1063
|
+
// Unsigned integers
|
|
1064
|
+
_.uint8 = dataViewField(DV.getUint8, DV.setUint8, 1, false);
|
|
1065
|
+
_.uint16 = dataViewField(DV.getUint16, DV.setUint16, 2, false);
|
|
1066
|
+
_.uint32 = dataViewField(DV.getUint32, DV.setUint32, 4, false);
|
|
1067
|
+
_.uint16le = dataViewField(DV.getUint16, DV.setUint16, 2, true);
|
|
1068
|
+
_.uint32le = dataViewField(DV.getUint32, DV.setUint32, 4, true);
|
|
1069
|
+
|
|
1070
|
+
// Signed integers
|
|
1071
|
+
_.int8 = dataViewField(DV.getInt8, DV.setInt8, 1, false);
|
|
1072
|
+
_.int16 = dataViewField(DV.getInt16, DV.setInt16, 2, false);
|
|
1073
|
+
_.int32 = dataViewField(DV.getInt32, DV.setInt32, 4, false);
|
|
1074
|
+
_.int16le = dataViewField(DV.getInt16, DV.setInt16, 2, true);
|
|
1075
|
+
_.int32le = dataViewField(DV.getInt32, DV.setInt32, 4, true);
|
|
1076
|
+
|
|
1077
|
+
// Floating-point numbers
|
|
1078
|
+
_.float32 = dataViewField(DV.getFloat32, DV.setFloat32, 4, false);
|
|
1079
|
+
_.float64 = dataViewField(DV.getFloat64, DV.setFloat64, 8, false);
|
|
1080
|
+
_.float32le = dataViewField(DV.getFloat32, DV.setFloat32, 4, true);
|
|
1081
|
+
_.float64le = dataViewField(DV.getFloat64, DV.setFloat64, 8, true);
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* Derives a new field based on an existing one with custom pack and unpack functions.
|
|
1085
|
+
* The types are intentionally any.
|
|
1086
|
+
* @param {Field} orig - The original field to derive from.
|
|
1087
|
+
* @param {function(*): *} pack - The function to pack the derived value.
|
|
1088
|
+
* @param {function(*): *} unpack - The function to unpack the derived value.
|
|
1089
|
+
* @returns {function((string|number)=, number=): Field} A function to create the derived field.
|
|
1090
|
+
*/
|
|
1091
|
+
_.derive = function (orig, pack, unpack) {
|
|
1092
|
+
/**
|
|
1093
|
+
* @param {string|number} [name]
|
|
1094
|
+
* @param {number} [count]
|
|
1095
|
+
* @returns {Field}
|
|
1096
|
+
*/
|
|
1097
|
+
var func = function (name, count) {
|
|
1098
|
+
/** @type {string|null} */
|
|
1099
|
+
var fieldName = null;
|
|
1100
|
+
if (typeof name === 'string') {
|
|
1101
|
+
fieldName = name;
|
|
1102
|
+
} else {
|
|
1103
|
+
count = name;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
var derived = /** @type {Field} */ (extend({
|
|
1107
|
+
/**
|
|
1108
|
+
* @param {BufferSource} buf
|
|
1109
|
+
* @param {Offset} [off]
|
|
1110
|
+
* @returns {*}
|
|
1111
|
+
*/
|
|
1112
|
+
unpack: function (buf, off) {
|
|
1113
|
+
var rawVal = orig.unpack(buf, off);
|
|
1114
|
+
return unpack(rawVal);
|
|
1115
|
+
},
|
|
1116
|
+
/**
|
|
1117
|
+
* @param {*} val
|
|
1118
|
+
* @param {BufferSource} buf
|
|
1119
|
+
* @param {Offset} [off]
|
|
1120
|
+
* @returns {Uint8Array}
|
|
1121
|
+
*/
|
|
1122
|
+
pack: function (val, buf, off) {
|
|
1123
|
+
var packed = pack(val);
|
|
1124
|
+
return orig.pack(packed, buf, off);
|
|
1125
|
+
},
|
|
1126
|
+
name: fieldName
|
|
1127
|
+
},
|
|
1128
|
+
('width' in orig)
|
|
1129
|
+
? { width: orig.width }
|
|
1130
|
+
: { size: orig.size }
|
|
1131
|
+
));
|
|
1132
|
+
|
|
1133
|
+
return arrayizeField(derived, count);
|
|
1134
|
+
};
|
|
1135
|
+
return func;
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
// Return the `_` object for whichever loader environment we're in.
|
|
1139
|
+
return _;
|
|
1140
|
+
}));
|