brs-js 1.4.1 → 2.0.2

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.
Files changed (87) hide show
  1. package/.prettierrc +7 -0
  2. package/LICENSE +1 -1
  3. package/README.md +2 -2
  4. package/debug/Conf Spacestation.brs +0 -0
  5. package/debug/Electrks_Feminine_Challenge.brs +0 -0
  6. package/debug/a5bricks.brs +0 -0
  7. package/debug/a5p2.2.brs +0 -0
  8. package/debug/a5p2.brs +0 -0
  9. package/debug/audio.brs +0 -0
  10. package/debug/brick a5.brs +0 -0
  11. package/debug/brick qa.brs +0 -0
  12. package/debug/brsv10.brs +0 -0
  13. package/debug/brsv10brick.brs +0 -0
  14. package/debug/bruteforce.js +254 -0
  15. package/debug/clone.html +36 -0
  16. package/debug/ctf_tileset.brs +0 -0
  17. package/debug/ctf_tileset_5.brs +0 -0
  18. package/debug/evil.brs +0 -0
  19. package/{src/write.js → debug/evilwrite.js} +218 -31
  20. package/debug/foo.txt +3080 -0
  21. package/debug/kenko_big.brs +0 -0
  22. package/debug/light.brs +0 -0
  23. package/debug/out.json +105 -0
  24. package/debug/read.js +30 -0
  25. package/debug/readSpeed.js +32 -0
  26. package/debug/readTest.js +45 -0
  27. package/debug/temp.brs +0 -0
  28. package/debug/western.brs +0 -0
  29. package/dist/dist.js +1 -1
  30. package/dist/dist.node.js +1 -1
  31. package/dist/dist.web.js +1 -1
  32. package/dist/src/constants.d.ts +6 -0
  33. package/dist/src/constants.d.ts.map +1 -0
  34. package/dist/src/index.d.ts +23 -0
  35. package/dist/src/index.d.ts.map +1 -0
  36. package/dist/src/read.d.ts +3 -0
  37. package/dist/src/read.d.ts.map +1 -0
  38. package/dist/src/read.v1.d.ts +3 -0
  39. package/dist/src/read.v1.d.ts.map +1 -0
  40. package/dist/src/read.v10.d.ts +3 -0
  41. package/dist/src/read.v10.d.ts.map +1 -0
  42. package/dist/src/read.v2.d.ts +3 -0
  43. package/dist/src/read.v2.d.ts.map +1 -0
  44. package/dist/src/read.v3.d.ts +3 -0
  45. package/dist/src/read.v3.d.ts.map +1 -0
  46. package/dist/src/read.v4.d.ts +3 -0
  47. package/dist/src/read.v4.d.ts.map +1 -0
  48. package/dist/src/read.v8.d.ts +3 -0
  49. package/dist/src/read.v8.d.ts.map +1 -0
  50. package/dist/src/read.v9.d.ts +3 -0
  51. package/dist/src/read.v9.d.ts.map +1 -0
  52. package/dist/src/types.d.ts +170 -0
  53. package/dist/src/types.d.ts.map +1 -0
  54. package/dist/src/utils.d.ts +87 -0
  55. package/dist/src/utils.d.ts.map +1 -0
  56. package/dist/src/uuid.d.ts +4 -0
  57. package/dist/src/uuid.d.ts.map +1 -0
  58. package/dist/src/write.d.ts +3 -0
  59. package/dist/src/write.d.ts.map +1 -0
  60. package/examples/ATCFort.brs +0 -0
  61. package/examples/read_example.html +34 -0
  62. package/examples/read_example.js +21 -0
  63. package/examples/write_example.js +25 -0
  64. package/examples/write_planet.html +144 -0
  65. package/examples/write_simplex.html +82 -0
  66. package/examples/write_wedge_sphere.html +173 -0
  67. package/package.json +17 -14
  68. package/src/constants.ts +6 -0
  69. package/src/{index.js → index.ts} +13 -1
  70. package/src/read.ts +57 -0
  71. package/src/read.v1.ts +99 -0
  72. package/src/read.v10.ts +185 -0
  73. package/src/read.v2.ts +102 -0
  74. package/src/read.v3.ts +111 -0
  75. package/src/read.v4.ts +112 -0
  76. package/src/read.v8.ts +172 -0
  77. package/src/read.v9.ts +181 -0
  78. package/src/types.ts +240 -0
  79. package/src/{utils.js → utils.ts} +241 -181
  80. package/src/{uuid.js → uuid.ts} +9 -5
  81. package/src/write.ts +209 -0
  82. package/test/lib.test.js +31 -23
  83. package/test/utils.test.js +50 -15
  84. package/tsconfig.json +22 -0
  85. package/webpack.config.js +51 -30
  86. package/src/constants.js +0 -4
  87. package/src/read.js +0 -206
@@ -1,10 +1,14 @@
1
- import pako from 'pako';
2
- import punycode from 'punycode';
3
- import {
4
- uuidStringify,
5
- uuidParse,
6
- } from './uuid';
1
+ import { deflate, inflate } from 'pako';
7
2
  import { MAX_INT } from './constants';
3
+ import {
4
+ BRSBytes,
5
+ Bytes,
6
+ UnrealColor,
7
+ UnrealFloat,
8
+ UnrealType,
9
+ Uuid,
10
+ } from './types';
11
+ import { uuidParse, uuidStringify } from './uuid';
8
12
 
9
13
  /*
10
14
  Notes:
@@ -16,66 +20,87 @@ import { MAX_INT } from './constants';
16
20
  */
17
21
 
18
22
  // Determine if a string is ascii-only
19
- function isASCII(str) {
20
- return /^[\x00-\x7F]*$/.test(str);
23
+ function isASCII(text: string): boolean {
24
+ return /^[\x00-\x7F]*$/.test(text);
21
25
  }
22
26
 
23
27
  // convert BGRA color to RGBA color
24
- const bgra = ([b, g, r, a]) => [r, g, b, a];
28
+ export const bgra = ([b, g, r, a]: number[]): [
29
+ number,
30
+ number,
31
+ number,
32
+ number
33
+ ] => [r, g, b, a];
25
34
 
26
35
  // Compare equality of byte arrays
27
- export function isEqual(arrA, arrB) {
28
- return arrA.every((a, i) => arrB[i] === a);
36
+ export function isEqual<T>(arrA: Array<T>, arrB: Array<T>): boolean {
37
+ return arrA.length === arrB.length && arrA.every((a: T, i) => arrB[i] === a);
29
38
  }
30
39
 
40
+ function isBRSBytes(data: Bytes): data is BRSBytes {
41
+ return (data as BRSBytes).brsOffset !== undefined;
42
+ }
31
43
 
32
44
  // read `len` bytes and return slice while updating offset
33
- function bytes(data, len, isCopy=false) {
45
+ export function subarray(data: Bytes, len: number, isCopy = false): Uint8Array {
34
46
  if (!(data instanceof Uint8Array)) {
35
47
  throw new Error(`Invalid data type in bytes reader (${typeof data})`);
36
48
  }
37
- if (typeof data.brsOffset === 'undefined')
38
- data.brsOffset = 0;
39
- const chunk = data[isCopy ? 'slice' : 'subarray'](data.brsOffset, data.brsOffset + len);
40
- chunk.brsOffset = 0;
41
- data.brsOffset += len;
49
+
50
+ let bytes: BRSBytes;
51
+ if (!isBRSBytes(data)) {
52
+ bytes = data as BRSBytes;
53
+ bytes.brsOffset = 0;
54
+ } else {
55
+ bytes = data;
56
+ }
57
+
58
+ const chunk = bytes[isCopy ? 'slice' : 'subarray'](
59
+ bytes.brsOffset,
60
+ bytes.brsOffset + len
61
+ );
62
+ bytes.brsOffset += len;
42
63
  return chunk;
43
64
  }
44
65
 
45
66
  // break a byte array into chunks of a specified size
46
- function chunk(arr, size) {
47
- const out = [];
67
+ export function chunk(arr: Bytes, size: number): BRSBytes[] {
68
+ // relative length based on the offset of the array's data view
69
+ const length = arr.length - (isBRSBytes(arr) ? arr.brsOffset : 0);
48
70
 
49
- for (let i = 0; i < arr.length; i += size) {
50
- out.push(bytes(arr, size, true));
71
+ // out array of chunks pre-allocated
72
+ const out = Array(Math.floor(length / size));
73
+
74
+ for (let i = 0; i < length / size; i++) {
75
+ out[i] = subarray(arr, size, true);
51
76
  }
52
77
 
53
78
  return out;
54
79
  }
55
80
 
56
81
  // Read a u16 from a byte array
57
- function read_u16(data, littleEndian=true) {
58
- const [a, b] = bytes(data, 2);
82
+ function read_u16(data: Bytes, littleEndian = true): number {
83
+ const [a, b] = subarray(data, 2);
59
84
 
60
- return littleEndian ? (b << 8 | a) : (a << 8 | b);
85
+ return littleEndian ? (b << 8) | a : (a << 8) | b;
61
86
  }
62
87
 
63
88
  // Write a u16 into byte array
64
- function write_u16(num, littleEndian=true) {
89
+ function write_u16(num: number, littleEndian = true): Uint8Array {
65
90
  const data = [num & 255, (num >> 8) & 255];
66
91
  return new Uint8Array(!littleEndian ? data.reverse() : data);
67
92
  }
68
93
 
69
94
  // Read an i32 from a byte array
70
- function read_i32(data, littleEndian=true) {
71
- const [a, b, c, d] = bytes(data, 4);
95
+ function read_i32(data: Bytes, littleEndian = true): number {
96
+ const [a, b, c, d] = subarray(data, 4);
72
97
  return littleEndian
73
- ? (d << 24 | c << 16 | b << 8 | a)
74
- : (a << 24 | b << 16 | c << 8 | d);
98
+ ? (d << 24) | (c << 16) | (b << 8) | a
99
+ : (a << 24) | (b << 16) | (c << 8) | d;
75
100
  }
76
101
 
77
102
  // Write an i32 from a byte array
78
- function write_i32(num, littleEndian=true) {
103
+ function write_i32(num: number, littleEndian = true): Uint8Array {
79
104
  const data = new Uint8Array([
80
105
  num & 255,
81
106
  (num >> 8) & 255,
@@ -87,32 +112,47 @@ function write_i32(num, littleEndian=true) {
87
112
  }
88
113
 
89
114
  // Decompress a byte array of compressed data
90
- function read_compressed(data) {
115
+ function read_compressed(data: Bytes): Bytes {
91
116
  const uncompressedSize = read_i32(data);
92
117
  const compressedSize = read_i32(data);
93
118
 
94
119
  // Throw error for weird compression/uncompression sizes
95
- if (compressedSize < 0 || uncompressedSize < 0 || compressedSize >= uncompressedSize) {
96
- throw new Error(`Invalid compressed section size (comp: ${compressedSize}, uncomp: ${uncompressedSize})`);
120
+ if (
121
+ compressedSize < 0 ||
122
+ uncompressedSize < 0 ||
123
+ compressedSize >= uncompressedSize
124
+ ) {
125
+ throw new Error(
126
+ `Invalid compressed section size (comp: ${compressedSize}, uncomp: ${uncompressedSize})`
127
+ );
97
128
  }
98
129
 
99
130
  // No compressed data? Return those bytes
100
131
  if (compressedSize === 0) {
101
- return bytes(data, uncompressedSize);
132
+ return subarray(data, uncompressedSize);
102
133
  } else {
103
134
  // Decompress the data otherwise
104
- const compressed = bytes(data, compressedSize);
105
- return pako.inflate(compressed);
135
+ const compressed = subarray(data, compressedSize);
136
+ return inflate(compressed);
106
137
  }
107
138
  }
108
139
 
109
140
  // Compress a byte array into fewer bytes
110
- function write_compressed(...args) {
141
+ function write_uncompressed(...args: Uint8Array[]): Uint8Array {
142
+ // Concat the args to one massive array
143
+ const data = concat(...args);
144
+
145
+ // Build the output
146
+ return concat(write_i32(data.length), write_i32(0), data);
147
+ }
148
+
149
+ // Compress a byte array into fewer bytes
150
+ function write_compressed(...args: Uint8Array[]): Uint8Array {
111
151
  // Concat the args to one massive array
112
152
  const data = concat(...args);
113
153
 
114
154
  // Do the compression
115
- const compressed = pako.deflate(data);
155
+ const compressed = deflate(data);
116
156
  const uncompressedSize = data.length;
117
157
  const compressedSize = compressed.length;
118
158
 
@@ -121,7 +161,7 @@ function write_compressed(...args) {
121
161
  }
122
162
 
123
163
  if (compressedSize > MAX_INT) {
124
- throw new Error(`compressedSize (${compression}) out of range`);
164
+ throw new Error(`compressedSize (${compressedSize}) out of range`);
125
165
  }
126
166
 
127
167
  // Determine if compression increases size
@@ -131,12 +171,12 @@ function write_compressed(...args) {
131
171
  return concat(
132
172
  write_i32(uncompressedSize),
133
173
  write_i32(badCompress ? 0 : compressedSize),
134
- badCompress ? data : compressed,
174
+ badCompress ? data : compressed
135
175
  );
136
176
  }
137
177
 
138
178
  // Read a string from a byte array
139
- function read_string(data) {
179
+ function read_string(data: Bytes): string {
140
180
  const raw_size = read_i32(data);
141
181
  const is_ucs2 = raw_size < 0;
142
182
  const size = is_ucs2 ? -raw_size : raw_size;
@@ -148,14 +188,13 @@ function read_string(data) {
148
188
  }
149
189
 
150
190
  // Create ucs2 encoded string
151
- return punycode.ucs2.encode(
191
+ return String.fromCodePoint(
152
192
  // Read the data in 2 byte windows
153
- chunk(bytes(data, size), 2)
154
- .map(arr => read_u16(arr)) // Convert the two bytes into u16
193
+ ...chunk(subarray(data, size), 2).map(arr => read_u16(arr)) // Convert the two bytes into u16
155
194
  );
156
195
  } else {
157
196
  // Read the data, remove the \u0000 at the end :)
158
- const strData = bytes(data, size).subarray(0, -1);
197
+ const strData = subarray(data, size).subarray(0, -1);
159
198
 
160
199
  // Convert into ascii
161
200
  return String.fromCharCode.apply(null, strData);
@@ -163,46 +202,48 @@ function read_string(data) {
163
202
  }
164
203
 
165
204
  // Write a string to bytes
166
- function write_string(str) {
205
+ function write_string(str: string): Uint8Array {
167
206
  if (isASCII(str)) {
168
207
  return concat(
169
208
  write_i32(str.length + 1), // Write string length (+ null term)
170
209
  new Uint8Array(str.split('').map(s => s.charCodeAt(0))), // Write string as bytes
171
- new Uint8Array([0]), // Null terminator
210
+ new Uint8Array([0]) // Null terminator
172
211
  );
173
212
  } else {
174
213
  // ucs2 strings denoted by negative length
175
214
  const len = -((str.length + 1) * 2);
176
215
  return concat(
177
216
  write_i32(len), // write length
178
- punycode.ucs2.decode(str), // write decoded string
179
- new Uint8Array([0]), // Null terminator
180
- )
217
+ // convert string to little endian ucs2
218
+ new Uint8Array(
219
+ str
220
+ .split('')
221
+ .flatMap(s => [s.charCodeAt(0) & 0xff, s.charCodeAt(0) >> 8])
222
+ ),
223
+ new Uint8Array([0]) // Null terminator
224
+ );
181
225
  }
182
226
  }
183
227
 
184
228
  // Read uuid from 4 LE ints
185
- function read_uuid(data) {
186
- return uuidStringify(chunk(bytes(data, 16), 4)
187
- .flatMap(arr => {
188
- arr.reverse();
189
- return Array.from(arr);
190
- })); // each chunk is LE
229
+ function read_uuid(data: Bytes): string {
230
+ return uuidStringify(
231
+ // each chunk is LE
232
+ chunk(subarray(data, 16), 4).flatMap(([a, b, c, d]) => [d, c, b, a])
233
+ );
191
234
  }
192
235
 
193
236
  // parse a uuid into 4 LE ints
194
- function write_uuid(uuid) {
237
+ function write_uuid(uuid: Uuid) {
195
238
  return concat(
196
- ...chunk(uuidParse(uuid), 4)
197
- .map(arr => {
198
- arr.reverse(); // convert into 4 LE ints;
199
- return new Uint8Array(arr);
200
- })
239
+ ...chunk(uuidParse(uuid), 4).map(
240
+ ([a, b, c, d]) => new Uint8Array([d, c, b, a])
241
+ )
201
242
  );
202
243
  }
203
244
 
204
245
  // Read an array of things given a fn
205
- function read_array(data, fn) {
246
+ function read_array<T>(data: Bytes, fn: (_: Bytes) => T): T[] {
206
247
  const length = read_i32(data);
207
248
  const arr = Array(length);
208
249
  for (let i = 0; i < length; i++) {
@@ -212,7 +253,7 @@ function read_array(data, fn) {
212
253
  }
213
254
 
214
255
  // iterate an array of things given a fn
215
- function read_each(data, fn) {
256
+ function read_each(data: Bytes, fn: (_: Bytes) => void) {
216
257
  const length = read_i32(data);
217
258
  for (let i = 0; i < length; i++) {
218
259
  fn(data);
@@ -220,26 +261,25 @@ function read_each(data, fn) {
220
261
  }
221
262
 
222
263
  // Write an array of things to bytes
223
- function write_array(arr, fn) {
224
- return concat(
225
- write_i32(arr.length),
226
- ...arr.map(o => fn(o))
227
- );
264
+ function write_array<T>(arr: T[], fn: (_: T) => Uint8Array) {
265
+ return concat(write_i32(arr.length), ...arr.map(o => fn(o)));
228
266
  }
229
267
 
230
268
  // Tool for reading byte arrays 1 bit at a time
231
269
  class BitReader {
232
- constructor(data) {
233
- this.buffer = new Uint8Array(data);
234
- this.pos = 0;
270
+ buffer: Uint8Array;
271
+ pos: number = 0;
272
+
273
+ constructor(data: Uint8Array) {
274
+ this.buffer = data;
235
275
  }
236
276
 
237
- empty() {
277
+ empty(): boolean {
238
278
  return this.pos >= this.buffer.length * 8;
239
279
  }
240
280
 
241
281
  // Read one bit as a boolean
242
- bit() {
282
+ bit(): boolean {
243
283
  const bit = (this.buffer[this.pos >> 3] & (1 << (this.pos & 0b111))) !== 0;
244
284
  this.pos++;
245
285
  return bit;
@@ -251,7 +291,7 @@ class BitReader {
251
291
  }
252
292
 
253
293
  // read an int up to max
254
- int(max) {
294
+ int(max: number): number {
255
295
  let value = 0;
256
296
  let mask = 1;
257
297
 
@@ -266,7 +306,7 @@ class BitReader {
266
306
  }
267
307
 
268
308
  // read a packet in from bits
269
- uint_packed() {
309
+ uint_packed(): number {
270
310
  let value = 0;
271
311
  for (let i = 0; i < 5; i++) {
272
312
  const next = this.bit();
@@ -285,58 +325,59 @@ class BitReader {
285
325
  }
286
326
 
287
327
  // an item in a read_positive_int_vector array
288
- int_packed() {
328
+ int_packed(): number {
289
329
  const value = this.uint_packed();
290
- return (value >> 1) * (value & 1 !== 0 ? 1 : -1);
330
+ return (value >> 1) * ((value & 1) !== 0 ? 1 : -1);
291
331
  }
292
332
 
293
333
  // read some bits
294
- bits(num) {
295
- const arr = [];
334
+ bits(num: number): number[] {
335
+ const arr: number[] = [];
296
336
  for (let bit = 0; bit < num; bit++) {
297
337
  const shift = bit & 7;
298
- arr[bit >> 3] = (arr[bit >> 3] & ~(1 << shift)) | ((this.bit() ? 1 : 0) << shift);
338
+ arr[bit >> 3] =
339
+ (arr[bit >> 3] & ~(1 << shift)) | ((this.bit() ? 1 : 0) << shift);
299
340
  }
300
341
  return arr;
301
342
  }
302
343
 
303
344
  // Read some bytes
304
- bytes(num) {
305
- return new Uint8Array(this.bits(num * 8));
345
+ bytes(num: number): Uint8Array {
346
+ return new Uint8Array(this.bytesArr(num));
306
347
  }
307
348
 
308
349
  // read some bytes but not as a Uint8Array
309
- bytesArr(num) {
350
+ bytesArr(num: number): number[] {
310
351
  return this.bits(num * 8);
311
352
  }
312
353
 
313
354
  // read an array
314
- array(fn) {
355
+ array<T>(fn: (_: BitReader) => T): T[] {
315
356
  const length = read_i32(this.bytes(4));
316
357
  const arr = Array(length);
317
- for(let i = 0; i < length; i++) {
358
+ for (let i = 0; i < length; i++) {
318
359
  arr[i] = fn(this);
319
360
  }
320
361
  return arr;
321
362
  }
322
363
 
323
364
  // for each
324
- each(fn) {
365
+ each(fn: (data: BitReader) => void) {
325
366
  const length = read_i32(this.bytes(4));
326
- for(let i = 0; i < length; i++) {
367
+ for (let i = 0; i < length; i++) {
327
368
  fn(this);
328
369
  }
329
370
  }
330
371
 
331
372
  // read a string
332
- string() {
373
+ string(): string {
333
374
  const lenBytes = this.bytesArr(4);
334
375
  const len = read_i32(new Uint8Array(lenBytes));
335
376
  return read_string(new Uint8Array(lenBytes.concat(this.bytesArr(len))));
336
377
  }
337
378
 
338
379
  // read a 32-bit float
339
- float() {
380
+ float(): number {
340
381
  const view = new DataView(new ArrayBuffer(4));
341
382
 
342
383
  // Write the ints to it
@@ -349,35 +390,33 @@ class BitReader {
349
390
  }
350
391
 
351
392
  // read unreal types
352
- unreal(type) {
353
- switch(type) {
354
- case 'Class': case 'Object':
355
- return this.string();
356
- case 'Boolean':
357
- return !!read_i32(new Uint8Array(this.bytes(4)));
358
- case 'Float':
359
- return this.float();
360
- case 'Color':
361
- return bgra(this.bytes(4));
362
- case 'Byte':
363
- return this.bytes(1)[0];
364
- case 'Rotator':
365
- return [this.float(), this.float(), this.float()];
393
+ unreal(type: string): UnrealType {
394
+ switch (type) {
395
+ case 'Class':
396
+ case 'Object':
397
+ return this.string();
398
+ case 'Boolean':
399
+ return !!read_i32(this.bytes(4));
400
+ case 'Float':
401
+ return this.float();
402
+ case 'Color':
403
+ return bgra(this.bytesArr(4)) as UnrealColor;
404
+ case 'Byte':
405
+ return this.bytes(1)[0];
406
+ case 'Rotator':
407
+ return [this.float(), this.float(), this.float()];
366
408
  }
367
409
  throw new Error('Unknown unreal type ' + type);
368
410
  }
369
411
  }
370
412
 
371
413
  class BitWriter {
372
- constructor() {
373
- this.buffer = [];
374
- this.cur = 0;
375
- this.bitNum = 0;
376
-
377
- }
414
+ buffer: number[] = [];
415
+ cur: number = 0;
416
+ bitNum: number = 0;
378
417
 
379
418
  // Write a boolean as a bit
380
- bit(val) {
419
+ bit(val: boolean) {
381
420
  this.cur |= (val ? 1 : 0) << this.bitNum;
382
421
  this.bitNum++;
383
422
  if (this.bitNum >= 8) {
@@ -386,14 +425,14 @@ class BitWriter {
386
425
  }
387
426
 
388
427
  // Write `len` bits from `src` bytes
389
- bits(src, len) {
428
+ bits(src: number[] | Uint8Array, len: number) {
390
429
  for (let bit = 0; bit < len; bit++) {
391
430
  this.bit((src[bit >> 3] & (1 << (bit & 7))) !== 0);
392
431
  }
393
432
  }
394
433
 
395
434
  // Write multiple bytes
396
- bytes(src) {
435
+ bytes(src: number[] | Uint8Array) {
397
436
  this.bits(src, 8 * src.length);
398
437
  }
399
438
 
@@ -407,19 +446,23 @@ class BitWriter {
407
446
  }
408
447
 
409
448
  // Write an int up to the potential max size
410
- int(value, max) {
449
+ int(value: number, max: number) {
411
450
  if (max < 2) {
412
- throw new Error(`Invalid input (BitWriter) -- max (${max}) must be at least 2`)
451
+ throw new Error(
452
+ `Invalid input (BitWriter) -- max (${max}) must be at least 2`
453
+ );
413
454
  }
414
455
 
415
456
  if (value >= max) {
416
- throw new Error(`Invalid input (BitWriter) -- value (${value}) is larger than max (${max})`)
457
+ throw new Error(
458
+ `Invalid input (BitWriter) -- value (${value}) is larger than max (${max})`
459
+ );
417
460
  }
418
461
 
419
462
  let new_val = 0;
420
463
  let mask = 1;
421
464
 
422
- while ((new_val + mask) < max && mask !== 0) {
465
+ while (new_val + mask < max && mask !== 0) {
423
466
  this.bit((value & mask) !== 0);
424
467
  if ((value & mask) !== 0) {
425
468
  new_val |= mask;
@@ -430,7 +473,7 @@ class BitWriter {
430
473
  }
431
474
 
432
475
  // Write a packed unsigned int
433
- uint_packed(value) {
476
+ uint_packed(value: number) {
434
477
  do {
435
478
  const src = value & 0b1111111;
436
479
  value >>= 7;
@@ -440,26 +483,28 @@ class BitWriter {
440
483
  }
441
484
 
442
485
  // Write a packed integer
443
- int_packed(value) {
486
+ int_packed(value: number) {
444
487
  this.uint_packed((Math.abs(value) << 1) | (value >= 0 ? 1 : 0));
445
488
  }
446
489
 
447
490
  // Return built buffer
448
- finish() {
491
+ finish(): Uint8Array {
449
492
  this.align();
450
- return this.buffer;
493
+ return new Uint8Array(this.buffer);
451
494
  }
452
495
 
453
496
  // Return built buffer (and include length)
454
497
  finishSection() {
455
498
  this.align();
456
- return concat(write_i32(this.buffer.length), this.buffer);
499
+ return concat(write_i32(this.buffer.length), new Uint8Array(this.buffer));
457
500
  }
458
501
 
459
502
  // write a string
460
- string(str) { this.bytes(write_string(str)); }
503
+ string(str: string) {
504
+ this.bytes(write_string(str));
505
+ }
461
506
 
462
- float(num) {
507
+ float(num: UnrealFloat) {
463
508
  // create a float array
464
509
  const floatArr = new Float32Array(1);
465
510
  // assign the number
@@ -469,96 +514,110 @@ class BitWriter {
469
514
  this.bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
470
515
  }
471
516
 
472
- // use
473
- self(fn) {
517
+ // run a function with `this` as a BitReader
518
+ self(fn: (this: BitWriter) => void) {
474
519
  fn.bind(this)();
475
520
  return this;
476
521
  }
477
522
 
478
523
  // write an array
479
- array(arr, fn) {
524
+ array<T>(arr: T[], fn: (this: BitWriter, item: T, index: number) => void) {
480
525
  this.bytes(write_i32(arr.length));
481
526
  arr.forEach(fn.bind(this));
482
527
  return this;
483
528
  }
484
529
 
485
530
  // write things from an array
486
- each(arr, fn) {
531
+ each<T>(arr: T[], fn: (this: BitWriter, item: T, index: number) => void) {
487
532
  arr.forEach(fn.bind(this));
488
533
  return this;
489
534
  }
490
535
 
491
536
  // write unreal types
492
- unreal(type, value) {
493
- switch(type) {
494
- case 'Class':
495
- if (typeof value !== 'string') {
496
- throw new Error(`writing unreal type Class, did not receive string (${value})`);
497
- }
498
- this.string(value);
499
- return;
500
- case 'Object':
501
- if (typeof value !== 'string') {
502
- throw new Error(`writing unreal type Object, did not receive string (${value})`);
503
- }
504
- this.string(value);
505
- return;
506
- case 'Boolean':
507
- if (typeof value !== 'boolean') {
508
- throw new Error(`writing unreal type Boolean, did not receive boolean (${value})`);
509
- }
510
- this.bytes(write_i32(value ? 1 : 0));
511
- return;
512
- case 'Float':
513
- if (typeof value !== 'number') {
514
- throw new Error(`writing unreal type Float, did not receive float (${value})`);
515
- }
516
- this.float(value);
517
- return;
518
- case 'Byte':
519
- if (typeof value !== 'number') {
520
- throw new Error(`writing unreal type Byte, did not receive Byte (${value})`);
521
- }
522
- this.bytes([value & 255]);
523
- return;
524
- case 'Color':
525
- if (typeof value !== 'object' && value.length === 4) {
526
- throw new Error(`writing unreal type Array, did not receive Array (${value})`);
527
- }
528
- this.bytes(bgra(value));
529
- return;
530
- case 'Rotator':
531
- if (typeof value !== 'object' && value.length === 3) {
532
- throw new Error(`writing unreal type Array, did not receive Array (${value})`);
533
- }
534
-
535
- this.float(value[0]);
536
- this.float(value[1]);
537
- this.float(value[2]);
538
- return;
537
+ unreal(type: string, value: UnrealType) {
538
+ switch (type) {
539
+ case 'Class':
540
+ if (typeof value !== 'string') {
541
+ throw new Error(
542
+ `writing unreal type Class, did not receive string (${value})`
543
+ );
544
+ }
545
+ this.string(value);
546
+ return;
547
+ case 'Object':
548
+ if (typeof value !== 'string') {
549
+ throw new Error(
550
+ `writing unreal type Object, did not receive string (${value})`
551
+ );
552
+ }
553
+ this.string(value);
554
+ return;
555
+ case 'Boolean':
556
+ if (typeof value !== 'boolean') {
557
+ throw new Error(
558
+ `writing unreal type Boolean, did not receive boolean (${value})`
559
+ );
560
+ }
561
+ this.bytes(write_i32(value ? 1 : 0));
562
+ return;
563
+ case 'Float':
564
+ if (typeof value !== 'number') {
565
+ throw new Error(
566
+ `writing unreal type Float, did not receive float (${value})`
567
+ );
568
+ }
569
+ this.float(value);
570
+ return;
571
+ case 'Byte':
572
+ if (typeof value !== 'number') {
573
+ throw new Error(
574
+ `writing unreal type Byte, did not receive Byte (${value})`
575
+ );
576
+ }
577
+ this.bytes([value & 255]);
578
+ return;
579
+ case 'Color':
580
+ if (!Array.isArray(value) || value.length !== 4) {
581
+ throw new Error(
582
+ `writing unreal type Array, did not receive Array (${value})`
583
+ );
584
+ }
585
+ this.bytes(bgra(value));
586
+ return;
587
+ case 'Rotator':
588
+ if (!Array.isArray(value) || value.length !== 3) {
589
+ throw new Error(
590
+ `writing unreal type Array, did not receive Array (${value})`
591
+ );
592
+ }
593
+
594
+ this.float(value[0]);
595
+ this.float(value[1]);
596
+ this.float(value[2]);
597
+ return;
539
598
  }
540
599
  throw new Error('Unknown unreal type ' + type);
541
600
  }
542
601
  }
543
602
 
544
603
  // concat uint8arrays together
545
- export function concat(...arrays) {
604
+ export function concat(...arrays: Uint8Array[]): Uint8Array {
546
605
  const buffLen = arrays.reduce((sum, value) => sum + value.length, 0);
547
606
  const buff = new Uint8Array(buffLen);
548
607
 
549
608
  // for each array - copy it over buff
550
609
  // next array is copied right after the previous one
551
610
  let length = 0;
552
- for(const array of arrays) {
611
+ for (const array of arrays) {
553
612
  buff.set(array, length);
554
613
  length += array.length;
555
614
  }
556
615
 
557
616
  return buff;
558
- };
617
+ }
559
618
 
560
619
  export const read = {
561
- bytes,
620
+ bytes: subarray,
562
621
  u16: read_u16,
563
622
  i32: read_i32,
564
623
  compressed: read_compressed,
@@ -566,15 +625,16 @@ export const read = {
566
625
  uuid: read_uuid,
567
626
  array: read_array,
568
627
  each: read_each,
569
- bits: data => new BitReader(data),
628
+ bits: (data: Bytes) => new BitReader(data),
570
629
  };
571
630
 
572
631
  export const write = {
573
632
  u16: write_u16,
574
633
  i32: write_i32,
575
634
  compressed: write_compressed,
635
+ uncompressed: write_uncompressed,
576
636
  string: write_string,
577
637
  uuid: write_uuid,
578
638
  array: write_array,
579
- bits: data => new BitWriter(),
639
+ bits: () => new BitWriter(),
580
640
  };