react-native-update-cli 2.4.2 → 2.6.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.
- package/README.md +2 -0
- package/README.zh-CN.md +2 -0
- package/cli.json +40 -0
- package/lib/api.js +1 -1
- package/lib/locales/en.js +16 -1
- package/lib/locales/zh.js +16 -1
- package/lib/package.js +60 -6
- package/lib/provider.js +3 -0
- package/lib/utils/app-info-parser/aab.js +230 -0
- package/lib/utils/app-info-parser/apk.js +25 -27
- package/lib/utils/app-info-parser/app.js +10 -11
- package/lib/utils/app-info-parser/index.js +13 -8
- package/lib/utils/app-info-parser/ipa.js +16 -21
- package/lib/utils/app-info-parser/resource-finder.js +365 -305
- package/lib/utils/app-info-parser/utils.js +78 -63
- package/lib/utils/app-info-parser/xml-parser/binary.js +57 -51
- package/lib/utils/app-info-parser/xml-parser/manifest.js +47 -39
- package/lib/utils/app-info-parser/zip.js +21 -11
- package/lib/utils/http-helper.js +1 -1
- package/lib/utils/index.js +137 -0
- package/lib/versions.js +22 -0
- package/package.json +3 -2
- package/proto/Configuration.proto +183 -0
- package/proto/Resources.proto +569 -0
- package/src/api.ts +2 -6
- package/src/locales/en.ts +20 -0
- package/src/locales/zh.ts +18 -0
- package/src/modules/version-module.ts +1 -1
- package/src/package.ts +112 -12
- package/src/provider.ts +3 -0
- package/src/utils/app-info-parser/aab.ts +240 -0
- package/src/utils/app-info-parser/{apk.js → apk.ts} +30 -41
- package/src/utils/app-info-parser/app.ts +3 -0
- package/src/utils/app-info-parser/index.ts +9 -5
- package/src/utils/app-info-parser/{ipa.js → ipa.ts} +17 -31
- package/src/utils/app-info-parser/resource-finder.ts +508 -0
- package/src/utils/app-info-parser/utils.ts +162 -0
- package/src/utils/app-info-parser/xml-parser/{binary.js → binary.ts} +69 -61
- package/src/utils/app-info-parser/xml-parser/{manifest.js → manifest.ts} +50 -51
- package/src/utils/app-info-parser/zip.ts +86 -0
- package/src/utils/dep-versions.ts +7 -4
- package/src/utils/http-helper.ts +1 -1
- package/src/utils/index.ts +154 -0
- package/src/utils/latest-version/index.ts +2 -1
- package/src/versions.ts +27 -2
- package/src/utils/app-info-parser/app.js +0 -16
- package/src/utils/app-info-parser/resource-finder.js +0 -495
- package/src/utils/app-info-parser/utils.js +0 -172
- package/src/utils/app-info-parser/zip.js +0 -66
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code translated from a C# project https://github.com/hylander0/Iteedee.ApkReader/blob/master/Iteedee.ApkReader/ApkResourceFinder.cs
|
|
3
|
+
*
|
|
4
|
+
* Decode binary file `resources.arsc` from a .apk file to a JavaScript Object.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const ByteBuffer = require('bytebuffer');
|
|
8
|
+
|
|
9
|
+
const DEBUG = false;
|
|
10
|
+
|
|
11
|
+
const RES_STRING_POOL_TYPE = 0x0001;
|
|
12
|
+
const RES_TABLE_TYPE = 0x0002;
|
|
13
|
+
const RES_TABLE_PACKAGE_TYPE = 0x0200;
|
|
14
|
+
const RES_TABLE_TYPE_TYPE = 0x0201;
|
|
15
|
+
const RES_TABLE_TYPE_SPEC_TYPE = 0x0202;
|
|
16
|
+
|
|
17
|
+
const TYPE_REFERENCE = 0x01;
|
|
18
|
+
const TYPE_STRING = 0x03;
|
|
19
|
+
|
|
20
|
+
export class ResourceFinder {
|
|
21
|
+
private valueStringPool: string[] | null = null;
|
|
22
|
+
private typeStringPool: string[] | null = null;
|
|
23
|
+
private keyStringPool: string[] | null = null;
|
|
24
|
+
|
|
25
|
+
private packageId = 0;
|
|
26
|
+
|
|
27
|
+
private responseMap: Record<string, any[]> = {};
|
|
28
|
+
private entryMap: Record<number, string[]> = {};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Same to C# BinaryReader.readBytes
|
|
32
|
+
*
|
|
33
|
+
* @param bb ByteBuffer
|
|
34
|
+
* @param len length
|
|
35
|
+
* @returns {Buffer}
|
|
36
|
+
*/
|
|
37
|
+
static readBytes(bb: any, len: number) {
|
|
38
|
+
const uint8Array = new Uint8Array(len);
|
|
39
|
+
for (let i = 0; i < len; i++) {
|
|
40
|
+
uint8Array[i] = bb.readUint8();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return ByteBuffer.wrap(uint8Array, 'binary', true);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
* @param {ByteBuffer} bb
|
|
49
|
+
* @return {Map<String, Set<String>>}
|
|
50
|
+
*/
|
|
51
|
+
processResourceTable(resourceBuffer: Buffer) {
|
|
52
|
+
const bb = ByteBuffer.wrap(resourceBuffer, 'binary', true);
|
|
53
|
+
|
|
54
|
+
const type = bb.readShort();
|
|
55
|
+
const headerSize = bb.readShort();
|
|
56
|
+
const size = bb.readInt();
|
|
57
|
+
const packageCount = bb.readInt();
|
|
58
|
+
let buffer: any;
|
|
59
|
+
let bb2: any;
|
|
60
|
+
|
|
61
|
+
if (type !== RES_TABLE_TYPE) {
|
|
62
|
+
throw new Error('No RES_TABLE_TYPE found!');
|
|
63
|
+
}
|
|
64
|
+
if (size !== bb.limit) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'The buffer size not matches to the resource table size.',
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
bb.offset = headerSize;
|
|
70
|
+
|
|
71
|
+
let realStringPoolCount = 0;
|
|
72
|
+
let realPackageCount = 0;
|
|
73
|
+
|
|
74
|
+
while (true) {
|
|
75
|
+
let pos = 0;
|
|
76
|
+
let t = 0;
|
|
77
|
+
let hs = 0;
|
|
78
|
+
let s = 0;
|
|
79
|
+
try {
|
|
80
|
+
pos = bb.offset;
|
|
81
|
+
t = bb.readShort();
|
|
82
|
+
hs = bb.readShort();
|
|
83
|
+
s = bb.readInt();
|
|
84
|
+
} catch (e) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
if (t === RES_STRING_POOL_TYPE) {
|
|
88
|
+
if (realStringPoolCount === 0) {
|
|
89
|
+
if (DEBUG) {
|
|
90
|
+
console.log('Processing the string pool ...');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
buffer = new ByteBuffer(s);
|
|
94
|
+
bb.offset = pos;
|
|
95
|
+
bb.prependTo(buffer);
|
|
96
|
+
|
|
97
|
+
bb2 = ByteBuffer.wrap(buffer, 'binary', true);
|
|
98
|
+
|
|
99
|
+
bb2.LE();
|
|
100
|
+
this.valueStringPool = this.processStringPool(bb2);
|
|
101
|
+
}
|
|
102
|
+
realStringPoolCount++;
|
|
103
|
+
} else if (t === RES_TABLE_PACKAGE_TYPE) {
|
|
104
|
+
if (DEBUG) {
|
|
105
|
+
console.log(`Processing the package ${realPackageCount} ...`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
buffer = new ByteBuffer(s);
|
|
109
|
+
bb.offset = pos;
|
|
110
|
+
bb.prependTo(buffer);
|
|
111
|
+
|
|
112
|
+
bb2 = ByteBuffer.wrap(buffer, 'binary', true);
|
|
113
|
+
bb2.LE();
|
|
114
|
+
this.processPackage(bb2);
|
|
115
|
+
|
|
116
|
+
realPackageCount++;
|
|
117
|
+
} else {
|
|
118
|
+
throw new Error('Unsupported type');
|
|
119
|
+
}
|
|
120
|
+
bb.offset = pos + s;
|
|
121
|
+
if (!bb.remaining()) break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (realStringPoolCount !== 1) {
|
|
125
|
+
throw new Error('More than 1 string pool found!');
|
|
126
|
+
}
|
|
127
|
+
if (realPackageCount !== packageCount) {
|
|
128
|
+
throw new Error('Real package count not equals the declared count.');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return this.responseMap;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
*
|
|
136
|
+
* @param {ByteBuffer} bb
|
|
137
|
+
*/
|
|
138
|
+
private processPackage(bb: any) {
|
|
139
|
+
const type = bb.readShort();
|
|
140
|
+
const headerSize = bb.readShort();
|
|
141
|
+
const size = bb.readInt();
|
|
142
|
+
const id = bb.readInt();
|
|
143
|
+
|
|
144
|
+
void type;
|
|
145
|
+
void size;
|
|
146
|
+
|
|
147
|
+
this.packageId = id;
|
|
148
|
+
|
|
149
|
+
for (let i = 0; i < 256; ++i) {
|
|
150
|
+
bb.readUint8();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const typeStrings = bb.readInt();
|
|
154
|
+
const lastPublicType = bb.readInt();
|
|
155
|
+
const keyStrings = bb.readInt();
|
|
156
|
+
const lastPublicKey = bb.readInt();
|
|
157
|
+
|
|
158
|
+
void lastPublicType;
|
|
159
|
+
void lastPublicKey;
|
|
160
|
+
|
|
161
|
+
if (typeStrings !== headerSize) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
'TypeStrings must immediately following the package structure header.',
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (DEBUG) {
|
|
168
|
+
console.log('Type strings:');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
let lastPosition = bb.offset;
|
|
172
|
+
bb.offset = typeStrings;
|
|
173
|
+
const bbTypeStrings = ResourceFinder.readBytes(bb, bb.limit - bb.offset);
|
|
174
|
+
bb.offset = lastPosition;
|
|
175
|
+
this.typeStringPool = this.processStringPool(bbTypeStrings);
|
|
176
|
+
|
|
177
|
+
if (DEBUG) {
|
|
178
|
+
console.log('Key strings:');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
bb.offset = keyStrings;
|
|
182
|
+
const keyType = bb.readShort();
|
|
183
|
+
const keyHeaderSize = bb.readShort();
|
|
184
|
+
const keySize = bb.readInt();
|
|
185
|
+
|
|
186
|
+
void keyType;
|
|
187
|
+
void keyHeaderSize;
|
|
188
|
+
|
|
189
|
+
lastPosition = bb.offset;
|
|
190
|
+
bb.offset = keyStrings;
|
|
191
|
+
const bbKeyStrings = ResourceFinder.readBytes(bb, bb.limit - bb.offset);
|
|
192
|
+
bb.offset = lastPosition;
|
|
193
|
+
this.keyStringPool = this.processStringPool(bbKeyStrings);
|
|
194
|
+
|
|
195
|
+
let typeSpecCount = 0;
|
|
196
|
+
let typeCount = 0;
|
|
197
|
+
|
|
198
|
+
bb.offset = keyStrings + keySize;
|
|
199
|
+
|
|
200
|
+
let bb2: any;
|
|
201
|
+
|
|
202
|
+
while (true) {
|
|
203
|
+
const pos = bb.offset;
|
|
204
|
+
try {
|
|
205
|
+
const t = bb.readShort();
|
|
206
|
+
const hs = bb.readShort();
|
|
207
|
+
const s = bb.readInt();
|
|
208
|
+
|
|
209
|
+
void hs;
|
|
210
|
+
|
|
211
|
+
if (t === RES_TABLE_TYPE_SPEC_TYPE) {
|
|
212
|
+
bb.offset = pos;
|
|
213
|
+
bb2 = ResourceFinder.readBytes(bb, s);
|
|
214
|
+
this.processTypeSpec(bb2);
|
|
215
|
+
|
|
216
|
+
typeSpecCount++;
|
|
217
|
+
} else if (t === RES_TABLE_TYPE_TYPE) {
|
|
218
|
+
bb.offset = pos;
|
|
219
|
+
bb2 = ResourceFinder.readBytes(bb, s);
|
|
220
|
+
this.processType(bb2);
|
|
221
|
+
|
|
222
|
+
typeCount++;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (s === 0) {
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
bb.offset = pos + s;
|
|
230
|
+
|
|
231
|
+
if (!bb.remaining()) {
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
} catch (e) {
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
void typeSpecCount;
|
|
240
|
+
void typeCount;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
*
|
|
245
|
+
* @param {ByteBuffer} bb
|
|
246
|
+
*/
|
|
247
|
+
private processType(bb: any) {
|
|
248
|
+
const type = bb.readShort();
|
|
249
|
+
const headerSize = bb.readShort();
|
|
250
|
+
const size = bb.readInt();
|
|
251
|
+
const id = bb.readByte();
|
|
252
|
+
const res0 = bb.readByte();
|
|
253
|
+
const res1 = bb.readShort();
|
|
254
|
+
const entryCount = bb.readInt();
|
|
255
|
+
const entriesStart = bb.readInt();
|
|
256
|
+
|
|
257
|
+
void type;
|
|
258
|
+
void size;
|
|
259
|
+
void res0;
|
|
260
|
+
void res1;
|
|
261
|
+
|
|
262
|
+
const refKeys: Record<string, number> = {};
|
|
263
|
+
|
|
264
|
+
const configSize = bb.readInt();
|
|
265
|
+
|
|
266
|
+
void configSize;
|
|
267
|
+
|
|
268
|
+
bb.offset = headerSize;
|
|
269
|
+
|
|
270
|
+
if (headerSize + entryCount * 4 !== entriesStart) {
|
|
271
|
+
throw new Error('HeaderSize, entryCount and entriesStart are not valid.');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const entryIndices = new Array<number>(entryCount);
|
|
275
|
+
for (let i = 0; i < entryCount; ++i) {
|
|
276
|
+
entryIndices[i] = bb.readInt();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
for (let i = 0; i < entryCount; ++i) {
|
|
280
|
+
if (entryIndices[i] === -1) continue;
|
|
281
|
+
|
|
282
|
+
const resourceId = (this.packageId << 24) | (id << 16) | i;
|
|
283
|
+
|
|
284
|
+
let entrySize = 0;
|
|
285
|
+
let entryFlag = 0;
|
|
286
|
+
let entryKey = 0;
|
|
287
|
+
try {
|
|
288
|
+
entrySize = bb.readShort();
|
|
289
|
+
entryFlag = bb.readShort();
|
|
290
|
+
entryKey = bb.readInt();
|
|
291
|
+
} catch (e) {
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
void entrySize;
|
|
296
|
+
|
|
297
|
+
const FLAG_COMPLEX = 0x0001;
|
|
298
|
+
if ((entryFlag & FLAG_COMPLEX) === 0) {
|
|
299
|
+
const valueSize = bb.readShort();
|
|
300
|
+
const valueRes0 = bb.readByte();
|
|
301
|
+
const valueDataType = bb.readByte();
|
|
302
|
+
const valueData = bb.readInt();
|
|
303
|
+
|
|
304
|
+
void valueSize;
|
|
305
|
+
void valueRes0;
|
|
306
|
+
|
|
307
|
+
const idStr = Number(resourceId).toString(16);
|
|
308
|
+
const keyStr = this.keyStringPool ? this.keyStringPool[entryKey] : '';
|
|
309
|
+
|
|
310
|
+
let data: string | null = null;
|
|
311
|
+
|
|
312
|
+
if (DEBUG) {
|
|
313
|
+
console.log(`Entry 0x${idStr}, key: ${keyStr}, simple value type: `);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const key = Number.parseInt(idStr, 16);
|
|
317
|
+
|
|
318
|
+
const entryArr = this.entryMap[key] ?? [];
|
|
319
|
+
entryArr.push(keyStr);
|
|
320
|
+
this.entryMap[key] = entryArr;
|
|
321
|
+
|
|
322
|
+
if (valueDataType === TYPE_STRING) {
|
|
323
|
+
data = this.valueStringPool ? this.valueStringPool[valueData] : null;
|
|
324
|
+
|
|
325
|
+
if (DEBUG && this.valueStringPool) {
|
|
326
|
+
console.log(`, data: ${this.valueStringPool[valueData]}`);
|
|
327
|
+
}
|
|
328
|
+
} else if (valueDataType === TYPE_REFERENCE) {
|
|
329
|
+
refKeys[idStr] = valueData;
|
|
330
|
+
} else {
|
|
331
|
+
data = `${valueData}`;
|
|
332
|
+
if (DEBUG) {
|
|
333
|
+
console.log(`, data: ${valueData}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
this.putIntoMap(`@${idStr}`, data);
|
|
338
|
+
} else {
|
|
339
|
+
const entryParent = bb.readInt();
|
|
340
|
+
const entryCountValue = bb.readInt();
|
|
341
|
+
|
|
342
|
+
void entryParent;
|
|
343
|
+
|
|
344
|
+
for (let j = 0; j < entryCountValue; ++j) {
|
|
345
|
+
const refName = bb.readInt();
|
|
346
|
+
const valueSize = bb.readShort();
|
|
347
|
+
const valueRes0 = bb.readByte();
|
|
348
|
+
const valueDataType = bb.readByte();
|
|
349
|
+
const valueData = bb.readInt();
|
|
350
|
+
|
|
351
|
+
void refName;
|
|
352
|
+
void valueSize;
|
|
353
|
+
void valueRes0;
|
|
354
|
+
void valueDataType;
|
|
355
|
+
void valueData;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (DEBUG) {
|
|
359
|
+
const keyStr = this.keyStringPool ? this.keyStringPool[entryKey] : '';
|
|
360
|
+
console.log(
|
|
361
|
+
`Entry 0x${Number(resourceId).toString(16)}, key: ${keyStr}, complex value, not printed.`,
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
for (const refKey in refKeys) {
|
|
368
|
+
const values =
|
|
369
|
+
this.responseMap[
|
|
370
|
+
`@${Number(refKeys[refKey]).toString(16).toUpperCase()}`
|
|
371
|
+
];
|
|
372
|
+
if (values != null && Object.keys(values).length < 1000) {
|
|
373
|
+
for (const value of values) {
|
|
374
|
+
this.putIntoMap(`@${refKey}`, value);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
*
|
|
382
|
+
* @param {ByteBuffer} bb
|
|
383
|
+
* @return {Array}
|
|
384
|
+
*/
|
|
385
|
+
private processStringPool(bb: any) {
|
|
386
|
+
const type = bb.readShort();
|
|
387
|
+
const headerSize = bb.readShort();
|
|
388
|
+
const size = bb.readInt();
|
|
389
|
+
const stringCount = bb.readInt();
|
|
390
|
+
const styleCount = bb.readInt();
|
|
391
|
+
const flags = bb.readInt();
|
|
392
|
+
const stringsStart = bb.readInt();
|
|
393
|
+
const stylesStart = bb.readInt();
|
|
394
|
+
|
|
395
|
+
void type;
|
|
396
|
+
void headerSize;
|
|
397
|
+
void size;
|
|
398
|
+
void styleCount;
|
|
399
|
+
void stylesStart;
|
|
400
|
+
|
|
401
|
+
const isUtf8 = (flags & 256) !== 0;
|
|
402
|
+
|
|
403
|
+
const offsets = new Array<number>(stringCount);
|
|
404
|
+
for (let i = 0; i < stringCount; ++i) {
|
|
405
|
+
offsets[i] = bb.readInt();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const strings = new Array<string>(stringCount);
|
|
409
|
+
|
|
410
|
+
for (let i = 0; i < stringCount; ++i) {
|
|
411
|
+
const pos = stringsStart + offsets[i];
|
|
412
|
+
bb.offset = pos;
|
|
413
|
+
|
|
414
|
+
strings[i] = '';
|
|
415
|
+
|
|
416
|
+
if (isUtf8) {
|
|
417
|
+
let u16len = bb.readUint8();
|
|
418
|
+
|
|
419
|
+
if ((u16len & 0x80) !== 0) {
|
|
420
|
+
u16len = ((u16len & 0x7f) << 8) + bb.readUint8();
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
let u8len = bb.readUint8();
|
|
424
|
+
if ((u8len & 0x80) !== 0) {
|
|
425
|
+
u8len = ((u8len & 0x7f) << 8) + bb.readUint8();
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (u8len > 0) {
|
|
429
|
+
const buffer = ResourceFinder.readBytes(bb, u8len);
|
|
430
|
+
try {
|
|
431
|
+
strings[i] = ByteBuffer.wrap(buffer, 'utf8', true).toString('utf8');
|
|
432
|
+
} catch (e) {
|
|
433
|
+
if (DEBUG) {
|
|
434
|
+
console.error(e);
|
|
435
|
+
console.log('Error when turning buffer to utf-8 string.');
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
} else {
|
|
439
|
+
strings[i] = '';
|
|
440
|
+
}
|
|
441
|
+
} else {
|
|
442
|
+
let u16len = bb.readUint16();
|
|
443
|
+
if ((u16len & 0x8000) !== 0) {
|
|
444
|
+
u16len = ((u16len & 0x7fff) << 16) + bb.readUint16();
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (u16len > 0) {
|
|
448
|
+
const len = u16len * 2;
|
|
449
|
+
const buffer = ResourceFinder.readBytes(bb, len);
|
|
450
|
+
try {
|
|
451
|
+
strings[i] = ByteBuffer.wrap(buffer, 'utf8', true).toString('utf8');
|
|
452
|
+
} catch (e) {
|
|
453
|
+
if (DEBUG) {
|
|
454
|
+
console.error(e);
|
|
455
|
+
console.log('Error when turning buffer to utf-8 string.');
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (DEBUG) {
|
|
462
|
+
console.log('Parsed value: {0}', strings[i]);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return strings;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
*
|
|
471
|
+
* @param {ByteBuffer} bb
|
|
472
|
+
*/
|
|
473
|
+
private processTypeSpec(bb: any) {
|
|
474
|
+
const type = bb.readShort();
|
|
475
|
+
const headerSize = bb.readShort();
|
|
476
|
+
const size = bb.readInt();
|
|
477
|
+
const id = bb.readByte();
|
|
478
|
+
const res0 = bb.readByte();
|
|
479
|
+
const res1 = bb.readShort();
|
|
480
|
+
const entryCount = bb.readInt();
|
|
481
|
+
|
|
482
|
+
void type;
|
|
483
|
+
void headerSize;
|
|
484
|
+
void size;
|
|
485
|
+
void res0;
|
|
486
|
+
void res1;
|
|
487
|
+
|
|
488
|
+
if (DEBUG && this.typeStringPool) {
|
|
489
|
+
console.log(`Processing type spec ${this.typeStringPool[id - 1]}...`);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const flags = new Array<number>(entryCount);
|
|
493
|
+
|
|
494
|
+
for (let i = 0; i < entryCount; ++i) {
|
|
495
|
+
flags[i] = bb.readInt();
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
private putIntoMap(resId: string, value: any) {
|
|
500
|
+
const key = resId.toUpperCase();
|
|
501
|
+
if (this.responseMap[key] == null) {
|
|
502
|
+
this.responseMap[key] = [];
|
|
503
|
+
}
|
|
504
|
+
if (value) {
|
|
505
|
+
this.responseMap[key].push(value);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
const objectType = (value: unknown): string =>
|
|
2
|
+
Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
|
|
3
|
+
|
|
4
|
+
const isArray = (value: unknown): value is unknown[] =>
|
|
5
|
+
objectType(value) === 'array';
|
|
6
|
+
|
|
7
|
+
const isObject = (value: unknown): value is Record<string, unknown> =>
|
|
8
|
+
objectType(value) === 'object';
|
|
9
|
+
|
|
10
|
+
const isPrimitive = (value: unknown): boolean =>
|
|
11
|
+
value === null ||
|
|
12
|
+
['boolean', 'number', 'string', 'undefined'].includes(objectType(value));
|
|
13
|
+
|
|
14
|
+
const isBrowser = (): boolean =>
|
|
15
|
+
typeof process === 'undefined' ||
|
|
16
|
+
Object.prototype.toString.call(process) !== '[object process]';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* map file place with resourceMap
|
|
20
|
+
* @param {Object} apkInfo // json info parsed from .apk file
|
|
21
|
+
* @param {Object} resourceMap // resourceMap
|
|
22
|
+
*/
|
|
23
|
+
const mapInfoResource = (
|
|
24
|
+
apkInfo: Record<string, any>,
|
|
25
|
+
resourceMap: Record<string, any>,
|
|
26
|
+
) => {
|
|
27
|
+
const iteratorObj = (obj: Record<string, any>) => {
|
|
28
|
+
for (const i in obj) {
|
|
29
|
+
if (isArray(obj[i])) {
|
|
30
|
+
iteratorArray(obj[i] as any[]);
|
|
31
|
+
} else if (isObject(obj[i])) {
|
|
32
|
+
iteratorObj(obj[i] as Record<string, any>);
|
|
33
|
+
} else if (isPrimitive(obj[i])) {
|
|
34
|
+
if (isResources(obj[i])) {
|
|
35
|
+
obj[i] = resourceMap[transKeyToMatchResourceMap(obj[i])];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const iteratorArray = (array: any[]) => {
|
|
42
|
+
const l = array.length;
|
|
43
|
+
for (let i = 0; i < l; i++) {
|
|
44
|
+
if (isArray(array[i])) {
|
|
45
|
+
iteratorArray(array[i] as any[]);
|
|
46
|
+
} else if (isObject(array[i])) {
|
|
47
|
+
iteratorObj(array[i] as Record<string, any>);
|
|
48
|
+
} else if (isPrimitive(array[i])) {
|
|
49
|
+
if (isResources(array[i])) {
|
|
50
|
+
array[i] = resourceMap[transKeyToMatchResourceMap(array[i])];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const isResources = (attrValue: unknown) => {
|
|
57
|
+
if (!attrValue) return false;
|
|
58
|
+
const value =
|
|
59
|
+
typeof attrValue === 'string' ? attrValue : attrValue.toString();
|
|
60
|
+
return value.indexOf('resourceId:') === 0;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const transKeyToMatchResourceMap = (resourceId: string) =>
|
|
64
|
+
`@${resourceId.replace('resourceId:0x', '').toUpperCase()}`;
|
|
65
|
+
|
|
66
|
+
iteratorObj(apkInfo);
|
|
67
|
+
return apkInfo;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* find .apk file's icon path from json info
|
|
72
|
+
* @param info // json info parsed from .apk file
|
|
73
|
+
*/
|
|
74
|
+
const findApkIconPath = (info: any) => {
|
|
75
|
+
if (!info.application.icon || !info.application.icon.splice) {
|
|
76
|
+
return '';
|
|
77
|
+
}
|
|
78
|
+
const rulesMap: Record<string, number> = {
|
|
79
|
+
mdpi: 48,
|
|
80
|
+
hdpi: 72,
|
|
81
|
+
xhdpi: 96,
|
|
82
|
+
xxdpi: 144,
|
|
83
|
+
xxxhdpi: 192,
|
|
84
|
+
};
|
|
85
|
+
const resultMap: Record<string, string> = {};
|
|
86
|
+
const maxDpiIcon = { dpi: 120, icon: '' };
|
|
87
|
+
|
|
88
|
+
for (const i in rulesMap) {
|
|
89
|
+
info.application.icon.some((icon: string) => {
|
|
90
|
+
if (icon && icon.indexOf(i) !== -1) {
|
|
91
|
+
resultMap[`application-icon-${rulesMap[i]}`] = icon;
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (
|
|
98
|
+
resultMap[`application-icon-${rulesMap[i]}`] &&
|
|
99
|
+
rulesMap[i] >= maxDpiIcon.dpi
|
|
100
|
+
) {
|
|
101
|
+
maxDpiIcon.dpi = rulesMap[i];
|
|
102
|
+
maxDpiIcon.icon = resultMap[`application-icon-${rulesMap[i]}`];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (Object.keys(resultMap).length === 0 || !maxDpiIcon.icon) {
|
|
107
|
+
maxDpiIcon.dpi = 120;
|
|
108
|
+
maxDpiIcon.icon = info.application.icon[0] || '';
|
|
109
|
+
resultMap['applicataion-icon-120'] = maxDpiIcon.icon;
|
|
110
|
+
}
|
|
111
|
+
return maxDpiIcon.icon;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* find .ipa file's icon path from json info
|
|
116
|
+
* @param info // json info parsed from .ipa file
|
|
117
|
+
*/
|
|
118
|
+
const findIpaIconPath = (info: any) => {
|
|
119
|
+
if (info.CFBundleIcons?.CFBundlePrimaryIcon?.CFBundleIconFiles?.length) {
|
|
120
|
+
return info.CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles[
|
|
121
|
+
info.CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles.length - 1
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
if (info.CFBundleIconFiles?.length) {
|
|
125
|
+
return info.CFBundleIconFiles[info.CFBundleIconFiles.length - 1];
|
|
126
|
+
}
|
|
127
|
+
return '.app/Icon.png';
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* transform buffer to base64
|
|
132
|
+
* @param {Buffer} buffer
|
|
133
|
+
*/
|
|
134
|
+
const getBase64FromBuffer = (buffer: Buffer | string) => {
|
|
135
|
+
const base64 =
|
|
136
|
+
typeof buffer === 'string' ? buffer : buffer.toString('base64');
|
|
137
|
+
return `data:image/png;base64,${base64}`;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 去除unicode空字符
|
|
142
|
+
* @param {String} str
|
|
143
|
+
*/
|
|
144
|
+
const decodeNullUnicode = (value: string | RegExp) => {
|
|
145
|
+
if (typeof value === 'string') {
|
|
146
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: <explanation>
|
|
147
|
+
return value.replace(/\u0000/g, '');
|
|
148
|
+
}
|
|
149
|
+
return value;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export {
|
|
153
|
+
isArray,
|
|
154
|
+
isObject,
|
|
155
|
+
isPrimitive,
|
|
156
|
+
isBrowser,
|
|
157
|
+
mapInfoResource,
|
|
158
|
+
findApkIconPath,
|
|
159
|
+
findIpaIconPath,
|
|
160
|
+
getBase64FromBuffer,
|
|
161
|
+
decodeNullUnicode,
|
|
162
|
+
};
|