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 +158 -27
- package/lib/cjs/src/reader.js +263 -24
- package/lib/cjs/src/reader.js.map +1 -1
- package/lib/cjs/src/writer.js +257 -13
- package/lib/cjs/src/writer.js.map +1 -1
- package/lib/cjs/types/src/reader.d.ts +45 -10
- package/lib/cjs/types/src/reader.d.ts.map +1 -1
- package/lib/cjs/types/src/writer.d.ts +38 -3
- package/lib/cjs/types/src/writer.d.ts.map +1 -1
- package/lib/esm/src/reader.js +263 -24
- package/lib/esm/src/reader.js.map +1 -1
- package/lib/esm/src/writer.js +257 -13
- package/lib/esm/src/writer.js.map +1 -1
- package/lib/esm/types/src/reader.d.ts +45 -10
- package/lib/esm/types/src/reader.d.ts.map +1 -1
- package/lib/esm/types/src/writer.d.ts +38 -3
- package/lib/esm/types/src/writer.d.ts.map +1 -1
- package/package.json +1 -1
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
|
-
###
|
|
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
|
|
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
|
-
//
|
|
33
|
-
function
|
|
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.
|
|
37
|
-
|
|
38
|
-
header.
|
|
39
|
-
br.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
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>
|
|
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
|
|
182
|
-
<td align="center" rowspan="2"><b>length of data from current position</b
|
|
183
|
-
<td rowspan="2">Returns data from current read position to supplied length. <br><b>Note:</b> Does not affect supplied data
|
|
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
|
|
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
|
|
package/lib/cjs/src/reader.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
153
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|