react-native-update-cli 1.30.3 → 1.31.0

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.
@@ -1,671 +1,573 @@
1
- 'use strict';
2
-
3
1
  // From https://github.com/openstf/adbkit-apkreader
2
+ "use strict";
4
3
  const NodeType = {
5
- ELEMENT_NODE: 1,
6
- ATTRIBUTE_NODE: 2,
7
- CDATA_SECTION_NODE: 4
4
+ ELEMENT_NODE: 1,
5
+ ATTRIBUTE_NODE: 2,
6
+ CDATA_SECTION_NODE: 4
8
7
  };
9
-
10
8
  const ChunkType = {
11
- NULL: 0x0000,
12
- STRING_POOL: 0x0001,
13
- TABLE: 0x0002,
14
- XML: 0x0003,
15
- XML_FIRST_CHUNK: 0x0100,
16
- XML_START_NAMESPACE: 0x0100,
17
- XML_END_NAMESPACE: 0x0101,
18
- XML_START_ELEMENT: 0x0102,
19
- XML_END_ELEMENT: 0x0103,
20
- XML_CDATA: 0x0104,
21
- XML_LAST_CHUNK: 0x017f,
22
- XML_RESOURCE_MAP: 0x0180,
23
- TABLE_PACKAGE: 0x0200,
24
- TABLE_TYPE: 0x0201,
25
- TABLE_TYPE_SPEC: 0x0202
9
+ NULL: 0x0000,
10
+ STRING_POOL: 0x0001,
11
+ TABLE: 0x0002,
12
+ XML: 0x0003,
13
+ XML_FIRST_CHUNK: 0x0100,
14
+ XML_START_NAMESPACE: 0x0100,
15
+ XML_END_NAMESPACE: 0x0101,
16
+ XML_START_ELEMENT: 0x0102,
17
+ XML_END_ELEMENT: 0x0103,
18
+ XML_CDATA: 0x0104,
19
+ XML_LAST_CHUNK: 0x017f,
20
+ XML_RESOURCE_MAP: 0x0180,
21
+ TABLE_PACKAGE: 0x0200,
22
+ TABLE_TYPE: 0x0201,
23
+ TABLE_TYPE_SPEC: 0x0202
26
24
  };
27
-
28
25
  const StringFlags = {
29
- SORTED: 1 << 0,
30
- UTF8: 1 << 8
31
-
32
- // Taken from android.util.TypedValue
33
- };const TypedValue = {
34
- COMPLEX_MANTISSA_MASK: 0x00ffffff,
35
- COMPLEX_MANTISSA_SHIFT: 0x00000008,
36
- COMPLEX_RADIX_0p23: 0x00000003,
37
- COMPLEX_RADIX_16p7: 0x00000001,
38
- COMPLEX_RADIX_23p0: 0x00000000,
39
- COMPLEX_RADIX_8p15: 0x00000002,
40
- COMPLEX_RADIX_MASK: 0x00000003,
41
- COMPLEX_RADIX_SHIFT: 0x00000004,
42
- COMPLEX_UNIT_DIP: 0x00000001,
43
- COMPLEX_UNIT_FRACTION: 0x00000000,
44
- COMPLEX_UNIT_FRACTION_PARENT: 0x00000001,
45
- COMPLEX_UNIT_IN: 0x00000004,
46
- COMPLEX_UNIT_MASK: 0x0000000f,
47
- COMPLEX_UNIT_MM: 0x00000005,
48
- COMPLEX_UNIT_PT: 0x00000003,
49
- COMPLEX_UNIT_PX: 0x00000000,
50
- COMPLEX_UNIT_SHIFT: 0x00000000,
51
- COMPLEX_UNIT_SP: 0x00000002,
52
- DENSITY_DEFAULT: 0x00000000,
53
- DENSITY_NONE: 0x0000ffff,
54
- TYPE_ATTRIBUTE: 0x00000002,
55
- TYPE_DIMENSION: 0x00000005,
56
- TYPE_FIRST_COLOR_INT: 0x0000001c,
57
- TYPE_FIRST_INT: 0x00000010,
58
- TYPE_FLOAT: 0x00000004,
59
- TYPE_FRACTION: 0x00000006,
60
- TYPE_INT_BOOLEAN: 0x00000012,
61
- TYPE_INT_COLOR_ARGB4: 0x0000001e,
62
- TYPE_INT_COLOR_ARGB8: 0x0000001c,
63
- TYPE_INT_COLOR_RGB4: 0x0000001f,
64
- TYPE_INT_COLOR_RGB8: 0x0000001d,
65
- TYPE_INT_DEC: 0x00000010,
66
- TYPE_INT_HEX: 0x00000011,
67
- TYPE_LAST_COLOR_INT: 0x0000001f,
68
- TYPE_LAST_INT: 0x0000001f,
69
- TYPE_NULL: 0x00000000,
70
- TYPE_REFERENCE: 0x00000001,
71
- TYPE_STRING: 0x00000003
26
+ SORTED: 1 << 0,
27
+ UTF8: 1 << 8
28
+ };
29
+ // Taken from android.util.TypedValue
30
+ const TypedValue = {
31
+ COMPLEX_MANTISSA_MASK: 0x00ffffff,
32
+ COMPLEX_MANTISSA_SHIFT: 0x00000008,
33
+ COMPLEX_RADIX_0p23: 0x00000003,
34
+ COMPLEX_RADIX_16p7: 0x00000001,
35
+ COMPLEX_RADIX_23p0: 0x00000000,
36
+ COMPLEX_RADIX_8p15: 0x00000002,
37
+ COMPLEX_RADIX_MASK: 0x00000003,
38
+ COMPLEX_RADIX_SHIFT: 0x00000004,
39
+ COMPLEX_UNIT_DIP: 0x00000001,
40
+ COMPLEX_UNIT_FRACTION: 0x00000000,
41
+ COMPLEX_UNIT_FRACTION_PARENT: 0x00000001,
42
+ COMPLEX_UNIT_IN: 0x00000004,
43
+ COMPLEX_UNIT_MASK: 0x0000000f,
44
+ COMPLEX_UNIT_MM: 0x00000005,
45
+ COMPLEX_UNIT_PT: 0x00000003,
46
+ COMPLEX_UNIT_PX: 0x00000000,
47
+ COMPLEX_UNIT_SHIFT: 0x00000000,
48
+ COMPLEX_UNIT_SP: 0x00000002,
49
+ DENSITY_DEFAULT: 0x00000000,
50
+ DENSITY_NONE: 0x0000ffff,
51
+ TYPE_ATTRIBUTE: 0x00000002,
52
+ TYPE_DIMENSION: 0x00000005,
53
+ TYPE_FIRST_COLOR_INT: 0x0000001c,
54
+ TYPE_FIRST_INT: 0x00000010,
55
+ TYPE_FLOAT: 0x00000004,
56
+ TYPE_FRACTION: 0x00000006,
57
+ TYPE_INT_BOOLEAN: 0x00000012,
58
+ TYPE_INT_COLOR_ARGB4: 0x0000001e,
59
+ TYPE_INT_COLOR_ARGB8: 0x0000001c,
60
+ TYPE_INT_COLOR_RGB4: 0x0000001f,
61
+ TYPE_INT_COLOR_RGB8: 0x0000001d,
62
+ TYPE_INT_DEC: 0x00000010,
63
+ TYPE_INT_HEX: 0x00000011,
64
+ TYPE_LAST_COLOR_INT: 0x0000001f,
65
+ TYPE_LAST_INT: 0x0000001f,
66
+ TYPE_NULL: 0x00000000,
67
+ TYPE_REFERENCE: 0x00000001,
68
+ TYPE_STRING: 0x00000003
72
69
  };
73
-
74
70
  class BinaryXmlParser {
75
- constructor(buffer, options = {}) {
76
- this.buffer = buffer;
77
- this.cursor = 0;
78
- this.strings = [];
79
- this.resources = [];
80
- this.document = null;
81
- this.parent = null;
82
- this.stack = [];
83
- this.debug = options.debug || false;
84
- }
85
-
86
- readU8() {
87
- this.debug && console.group('readU8');
88
- this.debug && console.debug('cursor:', this.cursor);
89
- const val = this.buffer[this.cursor];
90
- this.debug && console.debug('value:', val);
91
- this.cursor += 1;
92
- this.debug && console.groupEnd();
93
- return val;
94
- }
95
-
96
- readU16() {
97
- this.debug && console.group('readU16');
98
- this.debug && console.debug('cursor:', this.cursor);
99
- const val = this.buffer.readUInt16LE(this.cursor);
100
- this.debug && console.debug('value:', val);
101
- this.cursor += 2;
102
- this.debug && console.groupEnd();
103
- return val;
104
- }
105
-
106
- readS32() {
107
- this.debug && console.group('readS32');
108
- this.debug && console.debug('cursor:', this.cursor);
109
- const val = this.buffer.readInt32LE(this.cursor);
110
- this.debug && console.debug('value:', val);
111
- this.cursor += 4;
112
- this.debug && console.groupEnd();
113
- return val;
114
- }
115
-
116
- readU32() {
117
- this.debug && console.group('readU32');
118
- this.debug && console.debug('cursor:', this.cursor);
119
- const val = this.buffer.readUInt32LE(this.cursor);
120
- this.debug && console.debug('value:', val);
121
- this.cursor += 4;
122
- this.debug && console.groupEnd();
123
- return val;
124
- }
125
-
126
- readLength8() {
127
- this.debug && console.group('readLength8');
128
- let len = this.readU8();
129
- if (len & 0x80) {
130
- len = (len & 0x7f) << 8;
131
- len += this.readU8();
71
+ readU8() {
72
+ this.debug && console.group('readU8');
73
+ this.debug && console.debug('cursor:', this.cursor);
74
+ const val = this.buffer[this.cursor];
75
+ this.debug && console.debug('value:', val);
76
+ this.cursor += 1;
77
+ this.debug && console.groupEnd();
78
+ return val;
132
79
  }
133
- this.debug && console.debug('length:', len);
134
- this.debug && console.groupEnd();
135
- return len;
136
- }
137
-
138
- readLength16() {
139
- this.debug && console.group('readLength16');
140
- let len = this.readU16();
141
- if (len & 0x8000) {
142
- len = (len & 0x7fff) << 16;
143
- len += this.readU16();
80
+ readU16() {
81
+ this.debug && console.group('readU16');
82
+ this.debug && console.debug('cursor:', this.cursor);
83
+ const val = this.buffer.readUInt16LE(this.cursor);
84
+ this.debug && console.debug('value:', val);
85
+ this.cursor += 2;
86
+ this.debug && console.groupEnd();
87
+ return val;
144
88
  }
145
- this.debug && console.debug('length:', len);
146
- this.debug && console.groupEnd();
147
- return len;
148
- }
149
-
150
- readDimension() {
151
- this.debug && console.group('readDimension');
152
-
153
- const dimension = {
154
- value: null,
155
- unit: null,
156
- rawUnit: null
157
- };
158
-
159
- const value = this.readU32();
160
- const unit = dimension.value & 0xff;
161
-
162
- dimension.value = value >> 8;
163
- dimension.rawUnit = unit;
164
-
165
- switch (unit) {
166
- case TypedValue.COMPLEX_UNIT_MM:
167
- dimension.unit = 'mm';
168
- break;
169
- case TypedValue.COMPLEX_UNIT_PX:
170
- dimension.unit = 'px';
171
- break;
172
- case TypedValue.COMPLEX_UNIT_DIP:
173
- dimension.unit = 'dp';
174
- break;
175
- case TypedValue.COMPLEX_UNIT_SP:
176
- dimension.unit = 'sp';
177
- break;
178
- case TypedValue.COMPLEX_UNIT_PT:
179
- dimension.unit = 'pt';
180
- break;
181
- case TypedValue.COMPLEX_UNIT_IN:
182
- dimension.unit = 'in';
183
- break;
89
+ readS32() {
90
+ this.debug && console.group('readS32');
91
+ this.debug && console.debug('cursor:', this.cursor);
92
+ const val = this.buffer.readInt32LE(this.cursor);
93
+ this.debug && console.debug('value:', val);
94
+ this.cursor += 4;
95
+ this.debug && console.groupEnd();
96
+ return val;
184
97
  }
185
-
186
- this.debug && console.groupEnd();
187
-
188
- return dimension;
189
- }
190
-
191
- readFraction() {
192
- this.debug && console.group('readFraction');
193
-
194
- const fraction = {
195
- value: null,
196
- type: null,
197
- rawType: null
198
- };
199
-
200
- const value = this.readU32();
201
- const type = value & 0xf;
202
-
203
- fraction.value = this.convertIntToFloat(value >> 4);
204
- fraction.rawType = type;
205
-
206
- switch (type) {
207
- case TypedValue.COMPLEX_UNIT_FRACTION:
208
- fraction.type = '%';
209
- break;
210
- case TypedValue.COMPLEX_UNIT_FRACTION_PARENT:
211
- fraction.type = '%p';
212
- break;
98
+ readU32() {
99
+ this.debug && console.group('readU32');
100
+ this.debug && console.debug('cursor:', this.cursor);
101
+ const val = this.buffer.readUInt32LE(this.cursor);
102
+ this.debug && console.debug('value:', val);
103
+ this.cursor += 4;
104
+ this.debug && console.groupEnd();
105
+ return val;
213
106
  }
214
-
215
- this.debug && console.groupEnd();
216
-
217
- return fraction;
218
- }
219
-
220
- readHex24() {
221
- this.debug && console.group('readHex24');
222
- var val = (this.readU32() & 0xffffff).toString(16);
223
- this.debug && console.groupEnd();
224
- return val;
225
- }
226
-
227
- readHex32() {
228
- this.debug && console.group('readHex32');
229
- var val = this.readU32().toString(16);
230
- this.debug && console.groupEnd();
231
- return val;
232
- }
233
-
234
- readTypedValue() {
235
- this.debug && console.group('readTypedValue');
236
-
237
- const typedValue = {
238
- value: null,
239
- type: null,
240
- rawType: null
241
- };
242
-
243
- const start = this.cursor;
244
-
245
- let size = this.readU16();
246
- /* const zero = */this.readU8();
247
- const dataType = this.readU8();
248
-
249
- // Yes, there has been a real world APK where the size is malformed.
250
- if (size === 0) {
251
- size = 8;
107
+ readLength8() {
108
+ this.debug && console.group('readLength8');
109
+ let len = this.readU8();
110
+ if (len & 0x80) {
111
+ len = (len & 0x7f) << 8;
112
+ len += this.readU8();
113
+ }
114
+ this.debug && console.debug('length:', len);
115
+ this.debug && console.groupEnd();
116
+ return len;
252
117
  }
253
-
254
- typedValue.rawType = dataType;
255
-
256
- switch (dataType) {
257
- case TypedValue.TYPE_INT_DEC:
258
- typedValue.value = this.readS32();
259
- typedValue.type = 'int_dec';
260
- break;
261
- case TypedValue.TYPE_INT_HEX:
262
- typedValue.value = this.readS32();
263
- typedValue.type = 'int_hex';
264
- break;
265
- case TypedValue.TYPE_STRING:
266
- var ref = this.readS32();
267
- typedValue.value = ref > 0 ? this.strings[ref] : '';
268
- typedValue.type = 'string';
269
- break;
270
- case TypedValue.TYPE_REFERENCE:
271
- var id = this.readU32();
272
- typedValue.value = `resourceId:0x${id.toString(16)}`;
273
- typedValue.type = 'reference';
274
- break;
275
- case TypedValue.TYPE_INT_BOOLEAN:
276
- typedValue.value = this.readS32() !== 0;
277
- typedValue.type = 'boolean';
278
- break;
279
- case TypedValue.TYPE_NULL:
280
- this.readU32();
281
- typedValue.value = null;
282
- typedValue.type = 'null';
283
- break;
284
- case TypedValue.TYPE_INT_COLOR_RGB8:
285
- typedValue.value = this.readHex24();
286
- typedValue.type = 'rgb8';
287
- break;
288
- case TypedValue.TYPE_INT_COLOR_RGB4:
289
- typedValue.value = this.readHex24();
290
- typedValue.type = 'rgb4';
291
- break;
292
- case TypedValue.TYPE_INT_COLOR_ARGB8:
293
- typedValue.value = this.readHex32();
294
- typedValue.type = 'argb8';
295
- break;
296
- case TypedValue.TYPE_INT_COLOR_ARGB4:
297
- typedValue.value = this.readHex32();
298
- typedValue.type = 'argb4';
299
- break;
300
- case TypedValue.TYPE_DIMENSION:
301
- typedValue.value = this.readDimension();
302
- typedValue.type = 'dimension';
303
- break;
304
- case TypedValue.TYPE_FRACTION:
305
- typedValue.value = this.readFraction();
306
- typedValue.type = 'fraction';
307
- break;
308
- default:
309
- {
310
- const type = dataType.toString(16);
311
- console.debug(`Not sure what to do with typed value of type 0x${type}, falling back to reading an uint32.`);
312
- typedValue.value = this.readU32();
313
- typedValue.type = 'unknown';
118
+ readLength16() {
119
+ this.debug && console.group('readLength16');
120
+ let len = this.readU16();
121
+ if (len & 0x8000) {
122
+ len = (len & 0x7fff) << 16;
123
+ len += this.readU16();
314
124
  }
125
+ this.debug && console.debug('length:', len);
126
+ this.debug && console.groupEnd();
127
+ return len;
315
128
  }
316
-
317
- // Ensure we consume the whole value
318
- const end = start + size;
319
- if (this.cursor !== end) {
320
- const type = dataType.toString(16);
321
- const diff = end - this.cursor;
322
- console.debug(`Cursor is off by ${diff} bytes at ${this.cursor} at supposed end \
323
- of typed value of type 0x${type}. The typed value started at offset ${start} \
324
- and is supposed to end at offset ${end}. Ignoring the rest of the value.`);
325
- this.cursor = end;
129
+ readDimension() {
130
+ this.debug && console.group('readDimension');
131
+ const dimension = {
132
+ value: null,
133
+ unit: null,
134
+ rawUnit: null
135
+ };
136
+ const value = this.readU32();
137
+ const unit = dimension.value & 0xff;
138
+ dimension.value = value >> 8;
139
+ dimension.rawUnit = unit;
140
+ switch(unit){
141
+ case TypedValue.COMPLEX_UNIT_MM:
142
+ dimension.unit = 'mm';
143
+ break;
144
+ case TypedValue.COMPLEX_UNIT_PX:
145
+ dimension.unit = 'px';
146
+ break;
147
+ case TypedValue.COMPLEX_UNIT_DIP:
148
+ dimension.unit = 'dp';
149
+ break;
150
+ case TypedValue.COMPLEX_UNIT_SP:
151
+ dimension.unit = 'sp';
152
+ break;
153
+ case TypedValue.COMPLEX_UNIT_PT:
154
+ dimension.unit = 'pt';
155
+ break;
156
+ case TypedValue.COMPLEX_UNIT_IN:
157
+ dimension.unit = 'in';
158
+ break;
159
+ }
160
+ this.debug && console.groupEnd();
161
+ return dimension;
326
162
  }
327
-
328
- this.debug && console.groupEnd();
329
-
330
- return typedValue;
331
- }
332
-
333
- // https://twitter.com/kawasima/status/427730289201139712
334
- convertIntToFloat(int) {
335
- const buf = new ArrayBuffer(4);new Int32Array(buf)[0] = int;
336
- return new Float32Array(buf)[0];
337
- }
338
-
339
- readString(encoding) {
340
- this.debug && console.group('readString', encoding);
341
- switch (encoding) {
342
- case 'utf-8':
343
- var stringLength = this.readLength8(encoding);
344
- this.debug && console.debug('stringLength:', stringLength);
345
- var byteLength = this.readLength8(encoding);
346
- this.debug && console.debug('byteLength:', byteLength);
347
- var value = this.buffer.toString(encoding, this.cursor, this.cursor += byteLength);
348
- this.debug && console.debug('value:', value);
163
+ readFraction() {
164
+ this.debug && console.group('readFraction');
165
+ const fraction = {
166
+ value: null,
167
+ type: null,
168
+ rawType: null
169
+ };
170
+ const value = this.readU32();
171
+ const type = value & 0xf;
172
+ fraction.value = this.convertIntToFloat(value >> 4);
173
+ fraction.rawType = type;
174
+ switch(type){
175
+ case TypedValue.COMPLEX_UNIT_FRACTION:
176
+ fraction.type = '%';
177
+ break;
178
+ case TypedValue.COMPLEX_UNIT_FRACTION_PARENT:
179
+ fraction.type = '%p';
180
+ break;
181
+ }
349
182
  this.debug && console.groupEnd();
350
- return value;
351
- case 'ucs2':
352
- stringLength = this.readLength16(encoding);
353
- this.debug && console.debug('stringLength:', stringLength);
354
- byteLength = stringLength * 2;
355
- this.debug && console.debug('byteLength:', byteLength);
356
- value = this.buffer.toString(encoding, this.cursor, this.cursor += byteLength);
357
- this.debug && console.debug('value:', value);
183
+ return fraction;
184
+ }
185
+ readHex24() {
186
+ this.debug && console.group('readHex24');
187
+ var val = (this.readU32() & 0xffffff).toString(16);
358
188
  this.debug && console.groupEnd();
359
- return value;
360
- default:
361
- throw new Error(`Unsupported encoding '${encoding}'`);
189
+ return val;
362
190
  }
363
- }
364
-
365
- readChunkHeader() {
366
- this.debug && console.group('readChunkHeader');
367
- var header = {
368
- startOffset: this.cursor,
369
- chunkType: this.readU16(),
370
- headerSize: this.readU16(),
371
- chunkSize: this.readU32()
372
- };
373
- this.debug && console.debug('startOffset:', header.startOffset);
374
- this.debug && console.debug('chunkType:', header.chunkType);
375
- this.debug && console.debug('headerSize:', header.headerSize);
376
- this.debug && console.debug('chunkSize:', header.chunkSize);
377
- this.debug && console.groupEnd();
378
- return header;
379
- }
380
-
381
- readStringPool(header) {
382
- this.debug && console.group('readStringPool');
383
-
384
- header.stringCount = this.readU32();
385
- this.debug && console.debug('stringCount:', header.stringCount);
386
- header.styleCount = this.readU32();
387
- this.debug && console.debug('styleCount:', header.styleCount);
388
- header.flags = this.readU32();
389
- this.debug && console.debug('flags:', header.flags);
390
- header.stringsStart = this.readU32();
391
- this.debug && console.debug('stringsStart:', header.stringsStart);
392
- header.stylesStart = this.readU32();
393
- this.debug && console.debug('stylesStart:', header.stylesStart);
394
-
395
- if (header.chunkType !== ChunkType.STRING_POOL) {
396
- throw new Error('Invalid string pool header');
191
+ readHex32() {
192
+ this.debug && console.group('readHex32');
193
+ var val = this.readU32().toString(16);
194
+ this.debug && console.groupEnd();
195
+ return val;
397
196
  }
398
-
399
- const offsets = [];
400
- for (let i = 0, l = header.stringCount; i < l; ++i) {
401
- this.debug && console.debug('offset:', i);
402
- offsets.push(this.readU32());
197
+ readTypedValue() {
198
+ this.debug && console.group('readTypedValue');
199
+ const typedValue = {
200
+ value: null,
201
+ type: null,
202
+ rawType: null
203
+ };
204
+ const start = this.cursor;
205
+ let size = this.readU16();
206
+ /* const zero = */ this.readU8();
207
+ const dataType = this.readU8();
208
+ // Yes, there has been a real world APK where the size is malformed.
209
+ if (size === 0) {
210
+ size = 8;
211
+ }
212
+ typedValue.rawType = dataType;
213
+ switch(dataType){
214
+ case TypedValue.TYPE_INT_DEC:
215
+ typedValue.value = this.readS32();
216
+ typedValue.type = 'int_dec';
217
+ break;
218
+ case TypedValue.TYPE_INT_HEX:
219
+ typedValue.value = this.readS32();
220
+ typedValue.type = 'int_hex';
221
+ break;
222
+ case TypedValue.TYPE_STRING:
223
+ var ref = this.readS32();
224
+ typedValue.value = ref > 0 ? this.strings[ref] : '';
225
+ typedValue.type = 'string';
226
+ break;
227
+ case TypedValue.TYPE_REFERENCE:
228
+ var id = this.readU32();
229
+ typedValue.value = `resourceId:0x${id.toString(16)}`;
230
+ typedValue.type = 'reference';
231
+ break;
232
+ case TypedValue.TYPE_INT_BOOLEAN:
233
+ typedValue.value = this.readS32() !== 0;
234
+ typedValue.type = 'boolean';
235
+ break;
236
+ case TypedValue.TYPE_NULL:
237
+ this.readU32();
238
+ typedValue.value = null;
239
+ typedValue.type = 'null';
240
+ break;
241
+ case TypedValue.TYPE_INT_COLOR_RGB8:
242
+ typedValue.value = this.readHex24();
243
+ typedValue.type = 'rgb8';
244
+ break;
245
+ case TypedValue.TYPE_INT_COLOR_RGB4:
246
+ typedValue.value = this.readHex24();
247
+ typedValue.type = 'rgb4';
248
+ break;
249
+ case TypedValue.TYPE_INT_COLOR_ARGB8:
250
+ typedValue.value = this.readHex32();
251
+ typedValue.type = 'argb8';
252
+ break;
253
+ case TypedValue.TYPE_INT_COLOR_ARGB4:
254
+ typedValue.value = this.readHex32();
255
+ typedValue.type = 'argb4';
256
+ break;
257
+ case TypedValue.TYPE_DIMENSION:
258
+ typedValue.value = this.readDimension();
259
+ typedValue.type = 'dimension';
260
+ break;
261
+ case TypedValue.TYPE_FRACTION:
262
+ typedValue.value = this.readFraction();
263
+ typedValue.type = 'fraction';
264
+ break;
265
+ default:
266
+ {
267
+ const type = dataType.toString(16);
268
+ console.debug(`Not sure what to do with typed value of type 0x${type}, falling back to reading an uint32.`);
269
+ typedValue.value = this.readU32();
270
+ typedValue.type = 'unknown';
271
+ }
272
+ }
273
+ // Ensure we consume the whole value
274
+ const end = start + size;
275
+ if (this.cursor !== end) {
276
+ const type = dataType.toString(16);
277
+ const diff = end - this.cursor;
278
+ console.debug(`Cursor is off by ${diff} bytes at ${this.cursor} at supposed end \
279
+ of typed value of type 0x${type}. The typed value started at offset ${start} \
280
+ and is supposed to end at offset ${end}. Ignoring the rest of the value.`);
281
+ this.cursor = end;
282
+ }
283
+ this.debug && console.groupEnd();
284
+ return typedValue;
403
285
  }
404
-
405
- const sorted = (header.flags & StringFlags.SORTED) === StringFlags.SORTED;
406
- this.debug && console.debug('sorted:', sorted);
407
- const encoding = (header.flags & StringFlags.UTF8) === StringFlags.UTF8 ? 'utf-8' : 'ucs2';
408
- this.debug && console.debug('encoding:', encoding);
409
-
410
- const stringsStart = header.startOffset + header.stringsStart;
411
- this.cursor = stringsStart;
412
- for (let i = 0, l = header.stringCount; i < l; ++i) {
413
- this.debug && console.debug('string:', i);
414
- this.debug && console.debug('offset:', offsets[i]);
415
- this.cursor = stringsStart + offsets[i];
416
- this.strings.push(this.readString(encoding));
286
+ // https://twitter.com/kawasima/status/427730289201139712
287
+ convertIntToFloat(int) {
288
+ const buf = new ArrayBuffer(4);
289
+ new Int32Array(buf)[0] = int;
290
+ return new Float32Array(buf)[0];
417
291
  }
418
-
419
- // Skip styles
420
- this.cursor = header.startOffset + header.chunkSize;
421
-
422
- this.debug && console.groupEnd();
423
-
424
- return null;
425
- }
426
-
427
- readResourceMap(header) {
428
- this.debug && console.group('readResourceMap');
429
- const count = Math.floor((header.chunkSize - header.headerSize) / 4);
430
- for (let i = 0; i < count; ++i) {
431
- this.resources.push(this.readU32());
292
+ readString(encoding) {
293
+ this.debug && console.group('readString', encoding);
294
+ switch(encoding){
295
+ case 'utf-8':
296
+ var stringLength = this.readLength8(encoding);
297
+ this.debug && console.debug('stringLength:', stringLength);
298
+ var byteLength = this.readLength8(encoding);
299
+ this.debug && console.debug('byteLength:', byteLength);
300
+ var value = this.buffer.toString(encoding, this.cursor, this.cursor += byteLength);
301
+ this.debug && console.debug('value:', value);
302
+ this.debug && console.groupEnd();
303
+ return value;
304
+ case 'ucs2':
305
+ stringLength = this.readLength16(encoding);
306
+ this.debug && console.debug('stringLength:', stringLength);
307
+ byteLength = stringLength * 2;
308
+ this.debug && console.debug('byteLength:', byteLength);
309
+ value = this.buffer.toString(encoding, this.cursor, this.cursor += byteLength);
310
+ this.debug && console.debug('value:', value);
311
+ this.debug && console.groupEnd();
312
+ return value;
313
+ default:
314
+ throw new Error(`Unsupported encoding '${encoding}'`);
315
+ }
432
316
  }
433
- this.debug && console.groupEnd();
434
- return null;
435
- }
436
-
437
- readXmlNamespaceStart() /* header */{
438
- this.debug && console.group('readXmlNamespaceStart');
439
-
440
- /* const line = */this.readU32();
441
- /* const commentRef = */this.readU32();
442
- /* const prefixRef = */this.readS32();
443
- /* const uriRef = */this.readS32();
444
-
445
- // We don't currently care about the values, but they could
446
- // be accessed like so:
447
- //
448
- // namespaceURI.prefix = this.strings[prefixRef] // if prefixRef > 0
449
- // namespaceURI.uri = this.strings[uriRef] // if uriRef > 0
450
-
451
- this.debug && console.groupEnd();
452
-
453
- return null;
454
- }
455
-
456
- readXmlNamespaceEnd() /* header */{
457
- this.debug && console.group('readXmlNamespaceEnd');
458
-
459
- /* const line = */this.readU32();
460
- /* const commentRef = */this.readU32();
461
- /* const prefixRef = */this.readS32();
462
- /* const uriRef = */this.readS32();
463
-
464
- // We don't currently care about the values, but they could
465
- // be accessed like so:
466
- //
467
- // namespaceURI.prefix = this.strings[prefixRef] // if prefixRef > 0
468
- // namespaceURI.uri = this.strings[uriRef] // if uriRef > 0
469
-
470
- this.debug && console.groupEnd();
471
-
472
- return null;
473
- }
474
-
475
- readXmlElementStart() /* header */{
476
- this.debug && console.group('readXmlElementStart');
477
-
478
- const node = {
479
- namespaceURI: null,
480
- nodeType: NodeType.ELEMENT_NODE,
481
- nodeName: null,
482
- attributes: [],
483
- childNodes: []
484
-
485
- /* const line = */ };this.readU32();
486
- /* const commentRef = */this.readU32();
487
- const nsRef = this.readS32();
488
- const nameRef = this.readS32();
489
-
490
- if (nsRef > 0) {
491
- node.namespaceURI = this.strings[nsRef];
317
+ readChunkHeader() {
318
+ this.debug && console.group('readChunkHeader');
319
+ var header = {
320
+ startOffset: this.cursor,
321
+ chunkType: this.readU16(),
322
+ headerSize: this.readU16(),
323
+ chunkSize: this.readU32()
324
+ };
325
+ this.debug && console.debug('startOffset:', header.startOffset);
326
+ this.debug && console.debug('chunkType:', header.chunkType);
327
+ this.debug && console.debug('headerSize:', header.headerSize);
328
+ this.debug && console.debug('chunkSize:', header.chunkSize);
329
+ this.debug && console.groupEnd();
330
+ return header;
492
331
  }
493
-
494
- node.nodeName = this.strings[nameRef];
495
-
496
- /* const attrStart = */this.readU16();
497
- /* const attrSize = */this.readU16();
498
- const attrCount = this.readU16();
499
- /* const idIndex = */this.readU16();
500
- /* const classIndex = */this.readU16();
501
- /* const styleIndex = */this.readU16();
502
-
503
- for (let i = 0; i < attrCount; ++i) {
504
- node.attributes.push(this.readXmlAttribute());
332
+ readStringPool(header) {
333
+ this.debug && console.group('readStringPool');
334
+ header.stringCount = this.readU32();
335
+ this.debug && console.debug('stringCount:', header.stringCount);
336
+ header.styleCount = this.readU32();
337
+ this.debug && console.debug('styleCount:', header.styleCount);
338
+ header.flags = this.readU32();
339
+ this.debug && console.debug('flags:', header.flags);
340
+ header.stringsStart = this.readU32();
341
+ this.debug && console.debug('stringsStart:', header.stringsStart);
342
+ header.stylesStart = this.readU32();
343
+ this.debug && console.debug('stylesStart:', header.stylesStart);
344
+ if (header.chunkType !== ChunkType.STRING_POOL) {
345
+ throw new Error('Invalid string pool header');
346
+ }
347
+ const offsets = [];
348
+ for(let i = 0, l = header.stringCount; i < l; ++i){
349
+ this.debug && console.debug('offset:', i);
350
+ offsets.push(this.readU32());
351
+ }
352
+ const sorted = (header.flags & StringFlags.SORTED) === StringFlags.SORTED;
353
+ this.debug && console.debug('sorted:', sorted);
354
+ const encoding = (header.flags & StringFlags.UTF8) === StringFlags.UTF8 ? 'utf-8' : 'ucs2';
355
+ this.debug && console.debug('encoding:', encoding);
356
+ const stringsStart = header.startOffset + header.stringsStart;
357
+ this.cursor = stringsStart;
358
+ for(let i = 0, l = header.stringCount; i < l; ++i){
359
+ this.debug && console.debug('string:', i);
360
+ this.debug && console.debug('offset:', offsets[i]);
361
+ this.cursor = stringsStart + offsets[i];
362
+ this.strings.push(this.readString(encoding));
363
+ }
364
+ // Skip styles
365
+ this.cursor = header.startOffset + header.chunkSize;
366
+ this.debug && console.groupEnd();
367
+ return null;
368
+ }
369
+ readResourceMap(header) {
370
+ this.debug && console.group('readResourceMap');
371
+ const count = Math.floor((header.chunkSize - header.headerSize) / 4);
372
+ for(let i = 0; i < count; ++i){
373
+ this.resources.push(this.readU32());
374
+ }
375
+ this.debug && console.groupEnd();
376
+ return null;
377
+ }
378
+ readXmlNamespaceStart() {
379
+ this.debug && console.group('readXmlNamespaceStart');
380
+ /* const line = */ this.readU32();
381
+ /* const commentRef = */ this.readU32();
382
+ /* const prefixRef = */ this.readS32();
383
+ /* const uriRef = */ this.readS32();
384
+ // We don't currently care about the values, but they could
385
+ // be accessed like so:
386
+ //
387
+ // namespaceURI.prefix = this.strings[prefixRef] // if prefixRef > 0
388
+ // namespaceURI.uri = this.strings[uriRef] // if uriRef > 0
389
+ this.debug && console.groupEnd();
390
+ return null;
391
+ }
392
+ readXmlNamespaceEnd() {
393
+ this.debug && console.group('readXmlNamespaceEnd');
394
+ /* const line = */ this.readU32();
395
+ /* const commentRef = */ this.readU32();
396
+ /* const prefixRef = */ this.readS32();
397
+ /* const uriRef = */ this.readS32();
398
+ // We don't currently care about the values, but they could
399
+ // be accessed like so:
400
+ //
401
+ // namespaceURI.prefix = this.strings[prefixRef] // if prefixRef > 0
402
+ // namespaceURI.uri = this.strings[uriRef] // if uriRef > 0
403
+ this.debug && console.groupEnd();
404
+ return null;
505
405
  }
506
-
507
- if (this.document) {
508
- this.parent.childNodes.push(node);
509
- this.parent = node;
510
- } else {
511
- this.document = this.parent = node;
406
+ readXmlElementStart() {
407
+ this.debug && console.group('readXmlElementStart');
408
+ const node = {
409
+ namespaceURI: null,
410
+ nodeType: NodeType.ELEMENT_NODE,
411
+ nodeName: null,
412
+ attributes: [],
413
+ childNodes: []
414
+ };
415
+ /* const line = */ this.readU32();
416
+ /* const commentRef = */ this.readU32();
417
+ const nsRef = this.readS32();
418
+ const nameRef = this.readS32();
419
+ if (nsRef > 0) {
420
+ node.namespaceURI = this.strings[nsRef];
421
+ }
422
+ node.nodeName = this.strings[nameRef];
423
+ /* const attrStart = */ this.readU16();
424
+ /* const attrSize = */ this.readU16();
425
+ const attrCount = this.readU16();
426
+ /* const idIndex = */ this.readU16();
427
+ /* const classIndex = */ this.readU16();
428
+ /* const styleIndex = */ this.readU16();
429
+ for(let i = 0; i < attrCount; ++i){
430
+ node.attributes.push(this.readXmlAttribute());
431
+ }
432
+ if (this.document) {
433
+ this.parent.childNodes.push(node);
434
+ this.parent = node;
435
+ } else {
436
+ this.document = this.parent = node;
437
+ }
438
+ this.stack.push(node);
439
+ this.debug && console.groupEnd();
440
+ return node;
512
441
  }
513
-
514
- this.stack.push(node);
515
-
516
- this.debug && console.groupEnd();
517
-
518
- return node;
519
- }
520
-
521
- readXmlAttribute() {
522
- this.debug && console.group('readXmlAttribute');
523
-
524
- const attr = {
525
- namespaceURI: null,
526
- nodeType: NodeType.ATTRIBUTE_NODE,
527
- nodeName: null,
528
- name: null,
529
- value: null,
530
- typedValue: null
531
- };
532
-
533
- const nsRef = this.readS32();
534
- const nameRef = this.readS32();
535
- const valueRef = this.readS32();
536
-
537
- if (nsRef > 0) {
538
- attr.namespaceURI = this.strings[nsRef];
442
+ readXmlAttribute() {
443
+ this.debug && console.group('readXmlAttribute');
444
+ const attr = {
445
+ namespaceURI: null,
446
+ nodeType: NodeType.ATTRIBUTE_NODE,
447
+ nodeName: null,
448
+ name: null,
449
+ value: null,
450
+ typedValue: null
451
+ };
452
+ const nsRef = this.readS32();
453
+ const nameRef = this.readS32();
454
+ const valueRef = this.readS32();
455
+ if (nsRef > 0) {
456
+ attr.namespaceURI = this.strings[nsRef];
457
+ }
458
+ attr.nodeName = attr.name = this.strings[nameRef];
459
+ if (valueRef > 0) {
460
+ // some apk have versionName with special characters
461
+ if (attr.name === 'versionName') {
462
+ // only keep printable characters
463
+ // https://www.ascii-code.com/characters/printable-characters
464
+ this.strings[valueRef] = this.strings[valueRef].replace(/[^\x21-\x7E]/g, '');
465
+ }
466
+ attr.value = this.strings[valueRef];
467
+ }
468
+ attr.typedValue = this.readTypedValue();
469
+ this.debug && console.groupEnd();
470
+ return attr;
539
471
  }
540
-
541
- attr.nodeName = attr.name = this.strings[nameRef];
542
-
543
- if (valueRef > 0) {
544
- // some apk have versionName with special characters
545
- if (attr.name === 'versionName') {
546
- // only keep printable characters
547
- // https://www.ascii-code.com/characters/printable-characters
548
- this.strings[valueRef] = this.strings[valueRef].replace(/[^\x21-\x7E]/g, '');
549
- }
550
- attr.value = this.strings[valueRef];
472
+ readXmlElementEnd() {
473
+ this.debug && console.group('readXmlCData');
474
+ /* const line = */ this.readU32();
475
+ /* const commentRef = */ this.readU32();
476
+ /* const nsRef = */ this.readS32();
477
+ /* const nameRef = */ this.readS32();
478
+ this.stack.pop();
479
+ this.parent = this.stack[this.stack.length - 1];
480
+ this.debug && console.groupEnd();
481
+ return null;
551
482
  }
552
-
553
- attr.typedValue = this.readTypedValue();
554
-
555
- this.debug && console.groupEnd();
556
-
557
- return attr;
558
- }
559
-
560
- readXmlElementEnd() /* header */{
561
- this.debug && console.group('readXmlCData');
562
-
563
- /* const line = */this.readU32();
564
- /* const commentRef = */this.readU32();
565
- /* const nsRef = */this.readS32();
566
- /* const nameRef = */this.readS32();
567
-
568
- this.stack.pop();
569
- this.parent = this.stack[this.stack.length - 1];
570
-
571
- this.debug && console.groupEnd();
572
-
573
- return null;
574
- }
575
-
576
- readXmlCData() /* header */{
577
- this.debug && console.group('readXmlCData');
578
-
579
- const cdata = {
580
- namespaceURI: null,
581
- nodeType: NodeType.CDATA_SECTION_NODE,
582
- nodeName: '#cdata',
583
- data: null,
584
- typedValue: null
585
-
586
- /* const line = */ };this.readU32();
587
- /* const commentRef = */this.readU32();
588
- const dataRef = this.readS32();
589
-
590
- if (dataRef > 0) {
591
- cdata.data = this.strings[dataRef];
483
+ readXmlCData() {
484
+ this.debug && console.group('readXmlCData');
485
+ const cdata = {
486
+ namespaceURI: null,
487
+ nodeType: NodeType.CDATA_SECTION_NODE,
488
+ nodeName: '#cdata',
489
+ data: null,
490
+ typedValue: null
491
+ };
492
+ /* const line = */ this.readU32();
493
+ /* const commentRef = */ this.readU32();
494
+ const dataRef = this.readS32();
495
+ if (dataRef > 0) {
496
+ cdata.data = this.strings[dataRef];
497
+ }
498
+ cdata.typedValue = this.readTypedValue();
499
+ this.parent.childNodes.push(cdata);
500
+ this.debug && console.groupEnd();
501
+ return cdata;
592
502
  }
593
-
594
- cdata.typedValue = this.readTypedValue();
595
-
596
- this.parent.childNodes.push(cdata);
597
-
598
- this.debug && console.groupEnd();
599
-
600
- return cdata;
601
- }
602
-
603
- readNull(header) {
604
- this.debug && console.group('readNull');
605
- this.cursor += header.chunkSize - header.headerSize;
606
- this.debug && console.groupEnd();
607
- return null;
608
- }
609
-
610
- parse() {
611
- this.debug && console.group('BinaryXmlParser.parse');
612
-
613
- const xmlHeader = this.readChunkHeader();
614
- if (xmlHeader.chunkType !== ChunkType.XML) {
615
- throw new Error('Invalid XML header');
503
+ readNull(header) {
504
+ this.debug && console.group('readNull');
505
+ this.cursor += header.chunkSize - header.headerSize;
506
+ this.debug && console.groupEnd();
507
+ return null;
616
508
  }
617
-
618
- while (this.cursor < this.buffer.length) {
619
- this.debug && console.group('chunk');
620
- const start = this.cursor;
621
- const header = this.readChunkHeader();
622
- switch (header.chunkType) {
623
- case ChunkType.STRING_POOL:
624
- this.readStringPool(header);
625
- break;
626
- case ChunkType.XML_RESOURCE_MAP:
627
- this.readResourceMap(header);
628
- break;
629
- case ChunkType.XML_START_NAMESPACE:
630
- this.readXmlNamespaceStart(header);
631
- break;
632
- case ChunkType.XML_END_NAMESPACE:
633
- this.readXmlNamespaceEnd(header);
634
- break;
635
- case ChunkType.XML_START_ELEMENT:
636
- this.readXmlElementStart(header);
637
- break;
638
- case ChunkType.XML_END_ELEMENT:
639
- this.readXmlElementEnd(header);
640
- break;
641
- case ChunkType.XML_CDATA:
642
- this.readXmlCData(header);
643
- break;
644
- case ChunkType.NULL:
645
- this.readNull(header);
646
- break;
647
- default:
648
- throw new Error(`Unsupported chunk type '${header.chunkType}'`);
649
- }
650
-
651
- // Ensure we consume the whole chunk
652
- const end = start + header.chunkSize;
653
- if (this.cursor !== end) {
654
- const diff = end - this.cursor;
655
- const type = header.chunkType.toString(16);
656
- console.debug(`Cursor is off by ${diff} bytes at ${this.cursor} at supposed \
509
+ parse() {
510
+ this.debug && console.group('BinaryXmlParser.parse');
511
+ const xmlHeader = this.readChunkHeader();
512
+ if (xmlHeader.chunkType !== ChunkType.XML) {
513
+ throw new Error('Invalid XML header');
514
+ }
515
+ while(this.cursor < this.buffer.length){
516
+ this.debug && console.group('chunk');
517
+ const start = this.cursor;
518
+ const header = this.readChunkHeader();
519
+ switch(header.chunkType){
520
+ case ChunkType.STRING_POOL:
521
+ this.readStringPool(header);
522
+ break;
523
+ case ChunkType.XML_RESOURCE_MAP:
524
+ this.readResourceMap(header);
525
+ break;
526
+ case ChunkType.XML_START_NAMESPACE:
527
+ this.readXmlNamespaceStart(header);
528
+ break;
529
+ case ChunkType.XML_END_NAMESPACE:
530
+ this.readXmlNamespaceEnd(header);
531
+ break;
532
+ case ChunkType.XML_START_ELEMENT:
533
+ this.readXmlElementStart(header);
534
+ break;
535
+ case ChunkType.XML_END_ELEMENT:
536
+ this.readXmlElementEnd(header);
537
+ break;
538
+ case ChunkType.XML_CDATA:
539
+ this.readXmlCData(header);
540
+ break;
541
+ case ChunkType.NULL:
542
+ this.readNull(header);
543
+ break;
544
+ default:
545
+ throw new Error(`Unsupported chunk type '${header.chunkType}'`);
546
+ }
547
+ // Ensure we consume the whole chunk
548
+ const end = start + header.chunkSize;
549
+ if (this.cursor !== end) {
550
+ const diff = end - this.cursor;
551
+ const type = header.chunkType.toString(16);
552
+ console.debug(`Cursor is off by ${diff} bytes at ${this.cursor} at supposed \
657
553
  end of chunk of type 0x${type}. The chunk started at offset ${start} and is \
658
554
  supposed to end at offset ${end}. Ignoring the rest of the chunk.`);
659
- this.cursor = end;
660
- }
661
-
662
- this.debug && console.groupEnd();
555
+ this.cursor = end;
556
+ }
557
+ this.debug && console.groupEnd();
558
+ }
559
+ this.debug && console.groupEnd();
560
+ return this.document;
561
+ }
562
+ constructor(buffer, options = {}){
563
+ this.buffer = buffer;
564
+ this.cursor = 0;
565
+ this.strings = [];
566
+ this.resources = [];
567
+ this.document = null;
568
+ this.parent = null;
569
+ this.stack = [];
570
+ this.debug = options.debug || false;
663
571
  }
664
-
665
- this.debug && console.groupEnd();
666
-
667
- return this.document;
668
- }
669
572
  }
670
-
671
- module.exports = BinaryXmlParser;
573
+ module.exports = BinaryXmlParser;