bireader 1.0.12 → 1.0.14

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/README.md CHANGED
@@ -20,41 +20,154 @@ Supported data types are:
20
20
 
21
21
  Provides both CommonJS and ES modules.
22
22
 
23
- ### Quick Start
23
+ ### Example
24
24
 
25
25
  Import the reader or writer. Create a new parser with the data and start parsing.
26
26
 
27
- Includes all presents for quick parsing or programmable functions (examples below).
27
+ Includes presents for quick parsing or programmable functions (examples below).
28
28
 
29
29
  ```javascript
30
30
  import {bireader, biwriter} from 'bireader';
31
31
 
32
- //example of using mixed preset function naming and programmable calls
33
- function header_read(data){
32
+ //parse a webp file example
33
+ function parse_webp(data){
34
34
  const br = new bireader(data)
35
+ br.hexdump({supressUnicode:true}) //console.log data as hex
36
+
37
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
38
+ // 00000 52 49 46 46 98 3a 00 00 57 45 42 50 56 50 38 58 RIFF.:..WEBPVP8X
39
+ // 00010 0a 00 00 00 10 00 00 00 ff 00 00 ff 00 00 41 4c ..............AL
40
+ // 00020 50 48 26 10 00 00 01 19 45 6d 1b 49 4a 3b cf 0c PH&.....Em.IJ;..
41
+ // 00030 7f c0 7b 60 88 e8 ff 04 80 a2 82 65 56 d2 d2 86 ..{`.......eV...
42
+ // 00040 24 54 61 d0 83 8f 7f 0e 82 b6 6d e3 f0 a7 bd ed $Ta.......m.....
43
+ // 00050 87 10 11 13 40 3b 86 8f 26 4b d6 2a b7 6d 24 39 ....@;..&K.*.m$9
44
+ // 00060 52 4f fe 39 7f 3b 62 4e cc ec 9b 17 31 01 0c 24 RO.9.;bN....1..$
45
+ // 00070 49 89 23 e0 01 ab 52 64 e3 23 fc 61 db 76 cc 91 I.#...Rd.#.a.v..
46
+ // 00080 b6 7d fb 51 48 c5 69 db 4c 1b 63 db b6 ed b9 6d .}.QH.i.L.c....m
47
+ // 00090 db be 87 8d b1 6d db 9e b6 cd a4 d3 ee 24 95 54 .....m.......$.T
48
+ // 000a0 52 b8 8e 65 a9 eb 38 ce ab 52 75 9d 67 ff 75 2f R..e..8..Ru.g.u/
49
+ // 000b0 77 44 40 94 6d 25 6c 74 91 a8 88 86 58 9b da 6e wD@.m%lt....X..n
50
+
35
51
  const header = {}
36
- header.magic = br.cstring({length:4})
37
- const unsigned = header.magic == "foo" ? true : false
38
- header.ver = br.int32(unsigned, unsigned == true : "big" : "little")
39
- br.skip(2) //reserved
40
- if(header.ver == 10){
41
- br.endianness("big")
42
- header.file_size = br.size
43
- header.heigth = br.uint()
44
- header.width = br.quad()
45
- } else if(header.ver < 9) {
46
- br.le()
47
- header.file_size = br.ushort()
48
- header.heigth = br.uword()
49
- header.width = br.uint64()
50
- } else {
51
- throw new Error('Unknown version of ' + header.ver)
52
+ header.magic = br.string({length:4}) //RIFF
53
+ header.size = br.uint32le() //15000
54
+ header.fileSize = header.size + 8 //15008
55
+ header.payload = br.string({length:4}) //WEBP
56
+ header.format = br.string({length:4}) //VP8X
57
+ header.formatChunkSize = br.uint32le() //10
58
+ switch (header.format){
59
+ case "VP8 ":
60
+ header.formatType = "Lossy"
61
+ var read_size = 0
62
+ header.frame_tag = ubit24()
63
+ read_size += 3;
64
+ header.key_frame = header.frame_tag & 0x1;
65
+ header.version = (header.frame_tag >> 1) & 0x7;
66
+ header.show_frame = (header.frame_tag >> 4) & 0x1;
67
+ header.first_part_size = (header.frame_tag >> 5) & 0x7FFFF;
68
+ header.start_code = ubit24() //should be 2752925
69
+ header.horizontal_size_code = ubit16();
70
+ header.width = header.horizontal_size_code & 0x3FFF;
71
+ header.horizontal_scale = header.horizontal_size_code >> 14;
72
+ header.vertical_size_code = ubit16();
73
+ header.height = header.vertical_size_code & 0x3FFF;
74
+ header.vertical_scale = header.vertical_size_code >> 14;
75
+ read_size += 7;
76
+ header.VP8data = br.extract(header.formatChunkSize - read_size, true)
77
+ break;
78
+ case "VP8L":
79
+ header.formatType = "Lossless"
80
+ var read_size = 0
81
+ header.signature = br.ubyte() // should be 47
82
+ read_size += 1;
83
+ header.readWidth = ubit14()
84
+ header.width = header.readWidth+1;
85
+ header.readHeight = ubit14()
86
+ header.height = header.readHeight+1;
87
+ header.alpha_is_used = bit1()
88
+ header.version_number = ubit3()
89
+ read_size += 4;
90
+ header.data = br.extract(header.formatChunkSize - read_size, true)
91
+ break;
92
+ case "VP8X":
93
+ header.formatType = "Extended"
94
+ br.big() //switch to Big Endian bit read
95
+ header.rsv = br.bit2() //Reserved
96
+ header.I = br.bit1() //ICC profile
97
+ header.L = br.bit1() //Alpha
98
+ header.E = br.bit1() //Exif
99
+ header.X = br.bit1() //XMP
100
+ header.A = br.bit1() //Animation
101
+ header.R = br.bit1() //Reserved
102
+ br.little() //return to little
103
+ header.rsv2 = br.ubit24()
104
+ header.widthMinus1 = br.ubit24()
105
+ header.width = header.widthMinus1 + 1
106
+ header.heightMinus1 = br.ubit24()
107
+ header.height = header.heightMinus1 + 1
108
+ if(header.I)
109
+ {
110
+ header.ICCP = br.string({length:4}) // Should be ICCP
111
+ header.ICCPChunkSize = br.uint32()
112
+ header.ICCPData = br.extract(header.ICCPChunkSize, true)
113
+ }
114
+ if(header.L)
115
+ {
116
+ header.ALPH = br.string({length:4}) // Should be ALPH
117
+ header.ALPHChunkSize = br.uint32() //4134
118
+ header.ALPHData = br.extract(header.ALPHChunkSize, true)
119
+ }
120
+ if(header.A)
121
+ {
122
+ header.ANI = br.string({length:4}) // Should be ANIM or ANIF
123
+ header.ANIChunkSize = br.uint32()
124
+ if(header.ANI == "ANIM")
125
+ {
126
+ header.BGColor = br.uint32()
127
+ header.loopCount = br.ushort()
128
+ header.ANIMData = br.extract(header.ANIChunkSize, true)
129
+ } else
130
+ if (header.ANI == "ANIF")
131
+ {
132
+ header.FrameX = br.ubit24()
133
+ header.FrameY = br.ubit24()
134
+ header.readFrameWidth = br.ubit24()
135
+ header.readFrameHeight = br.ubit24()
136
+ header.frameWidth = readFrameWidth + 1
137
+ header.frameHeight = readFrameHeight + 1
138
+ header.duration = br.ubit24()
139
+ header.rsv3 = br.ubit6()
140
+ header.byte.B = br.bit1() //Blending
141
+ header.byte.D = br.bit1() //Disposal
142
+ header.frameData = br.extract(16, true)
143
+ header.ANIFData = br.extract(header.ANIChunkSize, true)
144
+ }
145
+ }
146
+ header.extFormatStr = br.string({length:4})
147
+ header.extChunkSize = br.uint32()
148
+ header.extData = br.extract(header.extChunkSize, true)
149
+ if(header.E)
150
+ {
151
+ header.EXIF = br.string({length:4}) // Should be EXIF
152
+ header.EXIFChunkSize = br.uint32()
153
+ header.EXIFData = br.extract(header.EXIFChunkSize, true)
154
+ }
155
+ if(header.X)
156
+ {
157
+ header.XMP = br.string({length:4}) // Should be XMP
158
+ header.XMPChunkSize = br.uint32()
159
+ header.XMPMetaData = br.extract(header.XMPChunkSize, true)
160
+ }
161
+ break;
162
+ default:
163
+ header.data = br.extract(header.formatChunkSize, true)
164
+ break;
52
165
  }
53
166
  br.finished()
54
167
  return header
55
168
  }
56
169
 
57
- function header_write(size, magic, ver, heigth, width){
170
+ function rite_webp(size, magic, ver, heigth, width){
58
171
  const data = new Uint8Array(size)
59
172
  const bw = new biwriter(data)
60
173
  bw.writeString(magic, {length:4})
@@ -117,7 +230,7 @@ Common functions for setup and movement shared by both (unless indicated).
117
230
  <tr>
118
231
  <td>Name</td>
119
232
  <td>skip(<b>bytes</b>, bits)</td>
120
- <td align="center" rowspan="2"><b>bytes to skip</b>, bits to skip</td>
233
+ <td align="center" rowspan="2"><b>bytes to skip from current position</b>, bits to skip</td>
121
234
  <td rowspan="2">Use negative to go back.<br><b>Note:</b> Remaining bits are dropped when returning to a byte read.</td>
122
235
  </tr>
123
236
  <tr>
@@ -127,7 +240,7 @@ Common functions for setup and movement shared by both (unless indicated).
127
240
  <tr>
128
241
  <td>Name</td>
129
242
  <td>goto(<b>byte</b>, bit)</td>
130
- <td align="center" rowspan="2"><b>Offset from current byte</b>, bit read position</td>
243
+ <td align="center" rowspan="2"><b>Byte offset from start</b>, bit offset from byte offset</td>
131
244
  <td rowspan="2"><b>Note:</b> Remaining bits are drop when returning to byte data.</td>
132
245
  </tr>
133
246
  <tr>
@@ -178,13 +291,13 @@ Common functions for setup and movement shared by both (unless indicated).
178
291
  </tr>
179
292
  <tr>
180
293
  <td>Name</td>
181
- <td>extract(<b>length</b>)</td>
182
- <td align="center" rowspan="2"><b>length of data from current position</b></td>
183
- <td rowspan="2">Returns data from current read position to supplied length. <br><b>Note:</b> Does not affect supplied data or current read position.</td>
294
+ <td>extract(<b>length</b>, consume)</td>
295
+ <td align="center" rowspan="2"><b>length of data from current position</b>, consume length and move offset (default false)</td>
296
+ <td rowspan="2">Returns data from current read position to supplied length. <br><b>Note:</b> Does not affect supplied data. Only moves current read position if consume is true.</td>
184
297
  </tr>
185
298
  <tr>
186
299
  <td>Aliases</td>
187
- <td>wrap(<b>length</b>)<br>lift(<b>length</b>)</td>
300
+ <td>wrap(<b>length</b>, consume)<br>lift(<b>length</b>, consume)</td>
188
301
  </tr>
189
302
  <tr>
190
303
  <td>Name</td>
@@ -206,6 +319,24 @@ Common functions for setup and movement shared by both (unless indicated).
206
319
  <td>Aliases</td>
207
320
  <td>close()<br>done()<br>finished()</td>
208
321
  </tr>
322
+ <tr>
323
+ <td>Name</td>
324
+ <td>hexdump({length, startByte, supressUnicode})</td>
325
+ <td align="center">Length of dump, Byte to start the dump, Supress unicode character preview for cleaner columns</td>
326
+ <td >Console logs data. Defaults to current position and 192 bytes in length. Will trigger on error unless turned off (see below)</td>
327
+ </tr>
328
+ <tr>
329
+ <td>Name</td>
330
+ <td>errorDumpOff()</td>
331
+ <td align="center" >None</td>
332
+ <td >Turns hexdump off on error (default true)</td>
333
+ </tr>
334
+ <tr>
335
+ <td>Name</td>
336
+ <td>errorDumpOn()</td>
337
+ <td align="center" rowspan="2">None</td>
338
+ <td rowspan="2">Turns hexdump on on error (default true)</td>
339
+ </tr>
209
340
  </tbody>
210
341
  </table>
211
342
 
@@ -14,7 +14,8 @@ class bireader {
14
14
  check_size(read_size, read_bits) {
15
15
  const new_off = this.offset + (read_size || 0) + Math.ceil((this.bitoffset + (read_bits || 0)) / 8);
16
16
  if (new_off > this.size) {
17
- throw new Error(`Reader reached end of data.`);
17
+ this.errorDump ? "[Error], hexdump:\n" + this.hexdump() : "";
18
+ throw new Error(`Reader reached end of data: reading to ` + new_off + " at " + this.offset + " of " + this.size);
18
19
  }
19
20
  }
20
21
  isBuffer(obj) {
@@ -37,6 +38,7 @@ class bireader {
37
38
  this.offset = 0;
38
39
  this.bitoffset = 0;
39
40
  this.size = 0;
41
+ this.errorDump = true;
40
42
  if (endianness != undefined && typeof endianness != "string") {
41
43
  throw new Error("Endian must be big or little");
42
44
  }
@@ -128,6 +130,7 @@ class bireader {
128
130
  skip(bytes, bits) {
129
131
  this.check_size(bytes || 0);
130
132
  if ((((bytes || 0) + this.offset) + Math.ceil((this.bitoffset + (bits || 0)) / 8)) > this.size) {
133
+ this.errorDump ? "[Error], hexdump:\n" + this.hexdump() : "";
131
134
  throw new Error("Seek outside of size of data: " + this.size);
132
135
  }
133
136
  this.bitoffset += (bits || 0) % 8;
@@ -149,8 +152,10 @@ class bireader {
149
152
  * @param {number} bit - bit to jump to (0-7)
150
153
  */
151
154
  goto(byte, bit) {
152
- if ((byte + Math.ceil((bit || 0) / 8)) > this.size) {
153
- throw new Error("Goto outside of size of data: " + this.size);
155
+ const new_size = (byte + Math.ceil((bit || 0) / 8));
156
+ if (new_size > this.size) {
157
+ this.errorDump ? "[Error], hexdump:\n" + this.hexdump() : "";
158
+ throw new Error("Goto outside of size of data: goto " + new_size + " of " + this.size);
154
159
  }
155
160
  this.offset = byte;
156
161
  this.bitoffset = (bit || 0) % 8;
@@ -255,6 +260,14 @@ class bireader {
255
260
  return this.offset;
256
261
  }
257
262
  /**
263
+ * Get the current byte position
264
+ *
265
+ * @return {number} current byte position
266
+ */
267
+ saveOffset() {
268
+ return this.offset;
269
+ }
270
+ /**
258
271
  * Truncates array from start to current position unless supplied
259
272
  * Note: Does not affect supplied data
260
273
  * @param {number} startOffset - Start location, default 0
@@ -262,6 +275,10 @@ class bireader {
262
275
  * @returns {Buffer|Uint8Array} ``Buffer`` or ``Uint8Array``
263
276
  */
264
277
  clip(startOffset, endOffset) {
278
+ if ((endOffset || this.offset) > this.size) {
279
+ this.errorDump ? "[Error], hexdump:\n" + this.hexdump() : "";
280
+ throw new Error("End offset outside of data: endOffset" + endOffset + " of " + this.size);
281
+ }
265
282
  return this.data.slice(startOffset || 0, endOffset || this.offset);
266
283
  }
267
284
  /**
@@ -272,6 +289,10 @@ class bireader {
272
289
  * @returns {Buffer|Uint8Array} ``Buffer`` or ``Uint8Array``
273
290
  */
274
291
  crop(startOffset, endOffset) {
292
+ if ((endOffset || this.offset) > this.size) {
293
+ this.errorDump ? "[Error], hexdump:\n" + this.hexdump() : "";
294
+ throw new Error("End offset outside of data: endOffset" + endOffset + " of " + this.size);
295
+ }
275
296
  return this.data.slice(startOffset || 0, endOffset || this.offset);
276
297
  }
277
298
  /**
@@ -282,6 +303,10 @@ class bireader {
282
303
  * @returns {Buffer|Uint8Array} ``Buffer`` or ``Uint8Array``
283
304
  */
284
305
  truncate(startOffset, endOffset) {
306
+ if ((endOffset || this.offset) > this.size) {
307
+ this.errorDump ? "[Error], hexdump:\n" + this.hexdump() : "";
308
+ throw new Error("End offset outside of data: endOffset" + endOffset + " of " + this.size);
309
+ }
285
310
  return this.data.slice(startOffset || 0, endOffset || this.offset);
286
311
  }
287
312
  /**
@@ -292,43 +317,49 @@ class bireader {
292
317
  * @returns {Buffer|Uint8Array} ``Buffer`` or ``Uint8Array``
293
318
  */
294
319
  slice(startOffset, endOffset) {
320
+ if ((endOffset || this.offset) > this.size) {
321
+ this.errorDump ? "[Error], hexdump:\n" + this.hexdump() : "";
322
+ throw new Error("End offset outside of data: endOffset" + endOffset + " of " + this.size);
323
+ }
295
324
  return this.data.slice(startOffset || 0, endOffset || this.offset);
296
325
  }
297
326
  /**
298
327
  * Extract array from current position to length supplied
299
328
  * Note: Does not affect supplied data
300
329
  * @param {number} length - length of data to copy from current offset
330
+ * @param {number} consume - moves offset to end of length
301
331
  * @returns {Buffer|Uint8Array} ``Buffer`` or ``Uint8Array``
302
332
  */
303
- extract(length) {
333
+ extract(length, consume) {
304
334
  if (this.offset + (length || 0) > this.size) {
305
- throw new Error("End offset outside of data: " + this.size);
335
+ this.errorDump ? "[Error], hexdump:\n" + this.hexdump() : "";
336
+ throw new Error("End offset outside of data: at " + this.offset + " reading " + length + " of" + this.size);
306
337
  }
307
- return this.data.slice(this.offset, this.offset + (length || 0));
338
+ const extract = this.data.slice(this.offset, Number(this.offset + (length || 0)));
339
+ if (consume) {
340
+ this.offset += length;
341
+ }
342
+ return extract;
308
343
  }
309
344
  /**
310
345
  * Extract array from current position to length supplied
311
346
  * Note: Does not affect supplied data
312
347
  * @param {number} length - length of data to copy from current offset
348
+ * @param {number} consume - moves offset to end of length
313
349
  * @returns {Buffer|Uint8Array} ``Buffer`` or ``Uint8Array``
314
350
  */
315
- wrap(length) {
316
- if (this.offset + (length || 0) > this.size) {
317
- throw new Error("End offset outside of data: " + this.size);
318
- }
319
- return this.data.slice(this.offset, this.offset + (length || 0));
351
+ wrap(length, consume) {
352
+ return this.extract(length, consume);
320
353
  }
321
354
  /**
322
355
  * Extract array from current position to length supplied
323
356
  * Note: Does not affect supplied data
324
357
  * @param {number} length - length of data to copy from current offset
358
+ * @param {number} consume - moves offset to end of length
325
359
  * @returns {Buffer|Uint8Array} ``Buffer`` or ``Uint8Array``
326
360
  */
327
- lift(length) {
328
- if (this.offset + (length || 0) > this.size) {
329
- throw new Error("End offset outside of data: " + this.size);
330
- }
331
- return this.data.slice(this.offset, this.offset + (length || 0));
361
+ lift(length, consume) {
362
+ return this.extract(length, consume);
332
363
  }
333
364
  /**
334
365
  * Returns current data
@@ -368,6 +399,212 @@ class bireader {
368
399
  finished() {
369
400
  this.data = [];
370
401
  }
402
+ /**
403
+ * Turn hexdump on error off, default on
404
+ */
405
+ errorDumpOff() {
406
+ this.errorDump = false;
407
+ }
408
+ /**
409
+ * Turn hexdump on error on, default on
410
+ */
411
+ errorDumpOn() {
412
+ this.errorDump = true;
413
+ }
414
+ /**
415
+ * Console logs data as hex dump
416
+ *
417
+ * @param {object} options - options object
418
+ * ```javascript
419
+ * {
420
+ * length: 192, // number of bytes to log, default 192 or end of data
421
+ * startByte: 0, // byte to start dump, default current position
422
+ * supressUnicode: false // Supress unicode character preview for cleaner columns
423
+ * }
424
+ * ```
425
+ */
426
+ hexdump(options) {
427
+ var length = options && options.length;
428
+ var startByte = options && options.startByte;
429
+ var supressUnicode = options && options.supressUnicode || false;
430
+ if ((startByte || 0) > this.size) {
431
+ this.errorDump ? "[Error], hexdump:\n" + this.hexdump() : "";
432
+ throw new Error("Hexdump start is outside of data size: " + startByte + " of " + this.size);
433
+ }
434
+ const start = startByte || this.offset;
435
+ const end = Math.min(start + (length || 192), this.size);
436
+ if (start + (length || 0) > this.size) {
437
+ this.errorDump ? "[Error], hexdump:\n" + this.hexdump() : "";
438
+ throw new Error("Hexdump amount is outside of data size: " + (start + (length || 0)) + " of " + end);
439
+ }
440
+ function hex_check(byte, bits) {
441
+ var value = 0;
442
+ for (var i = 0; i < bits;) {
443
+ var remaining = bits - i;
444
+ var bitOffset = 0;
445
+ var currentByte = byte;
446
+ var read = Math.min(remaining, 8 - bitOffset);
447
+ var mask, readBits;
448
+ mask = ~(0xFF << read);
449
+ readBits = (currentByte >> (8 - read - bitOffset)) & mask;
450
+ value <<= read;
451
+ value |= readBits;
452
+ i += read;
453
+ }
454
+ value = value >>> 0;
455
+ return value;
456
+ }
457
+ const rows = [];
458
+ var header = " 0 1 2 3 4 5 6 7 8 9 A B C D E F ";
459
+ var ending = "0123456789ABCDEF";
460
+ var addr = "";
461
+ for (let i = start; i < end; i += 16) {
462
+ addr = i.toString(16).padStart(5, '0');
463
+ var row = this.data?.slice(i, i + 16) || [];
464
+ var hex = Array.from(row, (byte) => byte.toString(16).padStart(2, '0')).join(' ');
465
+ rows.push(`${addr} ${hex.padEnd(47)} `);
466
+ }
467
+ let result = '';
468
+ let make_wide = false;
469
+ let i = start;
470
+ while (i < end) {
471
+ const byte = this.data[i];
472
+ if (byte < 32 || byte == 127) {
473
+ result += '.';
474
+ }
475
+ else if (byte < 127) {
476
+ // Valid UTF-8 start byte or single-byte character
477
+ // Convert the byte to a character and add it to the result
478
+ result += String.fromCharCode(byte);
479
+ }
480
+ else if (supressUnicode) {
481
+ result += '.';
482
+ }
483
+ else if (hex_check(byte, 1) == 0) {
484
+ //Byte 1
485
+ result += String.fromCharCode(byte);
486
+ }
487
+ else if (hex_check(byte, 3) == 6) {
488
+ //Byte 2
489
+ if (i + 1 <= end) {
490
+ //check second byte
491
+ const byte2 = this.data[i + 1];
492
+ if (hex_check(byte2, 2) == 2) {
493
+ const charCode = ((byte & 0x1f) << 6) | (byte2 & 0x3f);
494
+ i++;
495
+ make_wide = true;
496
+ const read = " " + String.fromCharCode(charCode);
497
+ result += read;
498
+ }
499
+ else {
500
+ result += ".";
501
+ }
502
+ }
503
+ else {
504
+ result += ".";
505
+ }
506
+ }
507
+ else if (hex_check(byte, 4) == 14) {
508
+ //Byte 3
509
+ if (i + 1 <= end) {
510
+ //check second byte
511
+ const byte2 = this.data[i + 1];
512
+ if (hex_check(byte2, 2) == 2) {
513
+ if (i + 2 <= end) {
514
+ //check third byte
515
+ const byte3 = this.data[i + 2];
516
+ if (hex_check(byte3, 2) == 2) {
517
+ const charCode = ((byte & 0x0f) << 12) |
518
+ ((byte2 & 0x3f) << 6) |
519
+ (byte3 & 0x3f);
520
+ i += 2;
521
+ make_wide = true;
522
+ const read = " " + String.fromCharCode(charCode);
523
+ result += read;
524
+ }
525
+ else {
526
+ i++;
527
+ result += " .";
528
+ }
529
+ }
530
+ else {
531
+ i++;
532
+ result += " .";
533
+ }
534
+ }
535
+ else {
536
+ result += ".";
537
+ }
538
+ }
539
+ else {
540
+ result += ".";
541
+ }
542
+ }
543
+ else if (hex_check(byte, 5) == 28) {
544
+ //Byte 4
545
+ if (i + 1 <= end) {
546
+ //check second byte
547
+ const byte2 = this.data[i + 1];
548
+ if (hex_check(byte2, 2) == 2) {
549
+ if (i + 2 <= end) {
550
+ //check third byte
551
+ const byte3 = this.data[i + 2];
552
+ if (hex_check(byte3, 2) == 2) {
553
+ if (i + 3 <= end) {
554
+ //check fourth byte
555
+ const byte4 = this.data[i + 2];
556
+ if (hex_check(byte4, 2) == 2) {
557
+ const charCode = (((byte4 & 0xFF) << 24) | ((byte3 & 0xFF) << 16) | ((byte2 & 0xFF) << 8) | (byte & 0xFF));
558
+ i += 3;
559
+ make_wide = true;
560
+ const read = " " + String.fromCharCode(charCode);
561
+ result += read;
562
+ }
563
+ else {
564
+ i += 2;
565
+ result += " .";
566
+ }
567
+ }
568
+ else {
569
+ i += 2;
570
+ result += " .";
571
+ }
572
+ }
573
+ else {
574
+ i++;
575
+ result += " .";
576
+ }
577
+ }
578
+ else {
579
+ i++;
580
+ result += " .";
581
+ }
582
+ }
583
+ else {
584
+ result += ".";
585
+ }
586
+ }
587
+ else {
588
+ result += ".";
589
+ }
590
+ }
591
+ else {
592
+ // Invalid UTF-8 byte, add a period to the result
593
+ result += '.';
594
+ }
595
+ i++;
596
+ }
597
+ const chunks = result.match(new RegExp(`.{1,${16}}`, 'g'));
598
+ chunks?.forEach((self, i) => {
599
+ rows[i] = rows[i] + (make_wide ? "|" + self + "|" : self);
600
+ });
601
+ header = "".padStart(addr.length) + header + (make_wide ? "" : ending);
602
+ rows.unshift(header);
603
+ if (make_wide) {
604
+ rows.push("*Removed character byte header on unicode detection");
605
+ }
606
+ console.log(rows.join("\n"));
607
+ }
371
608
  //
372
609
  //bit reader
373
610
  //
@@ -390,6 +627,7 @@ class bireader {
390
627
  }
391
628
  const size_needed = ((((bits - 1) + this.bitoffset) / 8) + this.offset);
392
629
  if (bits <= 0 || size_needed > this.size) {
630
+ this.errorDump ? "[Error], hexdump:\n" + this.hexdump() : "";
393
631
  throw new Error("Invalid number of bits to read: " + size_needed + " of " + this.size);
394
632
  }
395
633
  var off_in_bits = (this.offset * 8) + this.bitoffset;
@@ -416,7 +654,7 @@ class bireader {
416
654
  }
417
655
  this.offset = this.offset + Math.floor(((bits) + this.bitoffset) / 8); //end byte
418
656
  this.bitoffset = ((bits) + this.bitoffset) % 8;
419
- if (unsigned == true) {
657
+ if (unsigned == true || bits <= 7) {
420
658
  return value >>> 0;
421
659
  }
422
660
  if (bits !== 32 && value & (1 << (bits - 1))) {
@@ -438,13 +676,13 @@ class bireader {
438
676
  return this.readBit(bits, unsigned, endian);
439
677
  }
440
678
  /**
441
- * Bit field reader
442
- *
443
- * Note: When returning to a byte read, remaining bits are dropped
444
- *
445
- * @param {boolean} unsigned - if the value is unsigned
446
- * @returns number
447
- */
679
+ * Bit field reader
680
+ *
681
+ * Note: When returning to a byte read, remaining bits are dropped
682
+ *
683
+ * @param {boolean} unsigned - if the value is unsigned
684
+ * @returns number
685
+ */
448
686
  bit1(unsigned) {
449
687
  return this.bit(1, unsigned);
450
688
  }
@@ -3724,6 +3962,7 @@ class bireader {
3724
3962
  maxBytes = this.readInt32(true, endian);
3725
3963
  }
3726
3964
  else {
3965
+ this.errorDump ? "[Error], hexdump:\n" + this.hexdump() : "";
3727
3966
  throw new Error("Invalid length read size: " + lengthReadSize);
3728
3967
  }
3729
3968
  // Read the string as Pascal or Delphi encoded