react-native-update-cli 2.7.1 → 2.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.json +1 -0
- package/lib/esm/api..mjs +183 -0
- package/lib/esm/app..mjs +130 -0
- package/lib/esm/bundle..mjs +823 -0
- package/lib/esm/exports..mjs +11 -0
- package/lib/esm/index..mjs +122 -0
- package/lib/esm/install..mjs +18 -0
- package/lib/esm/locales/en..mjs +131 -0
- package/lib/esm/locales/zh..mjs +130 -0
- package/lib/esm/module-manager..mjs +109 -0
- package/lib/esm/modules/app-module..mjs +213 -0
- package/lib/esm/modules/bundle-module..mjs +178 -0
- package/lib/esm/modules/index..mjs +17 -0
- package/lib/esm/modules/package-module..mjs +6 -0
- package/lib/esm/modules/user-module..mjs +351 -0
- package/lib/esm/modules/version-module..mjs +6 -0
- package/lib/esm/package..mjs +316 -0
- package/lib/esm/provider..mjs +293 -0
- package/lib/esm/types..mjs +1 -0
- package/lib/esm/user..mjs +36 -0
- package/lib/esm/utils/add-gitignore..mjs +32 -0
- package/lib/esm/utils/app-info-parser/aab..mjs +215 -0
- package/lib/esm/utils/app-info-parser/apk..mjs +75 -0
- package/lib/esm/utils/app-info-parser/app..mjs +3 -0
- package/lib/esm/utils/app-info-parser/index..mjs +44 -0
- package/lib/esm/utils/app-info-parser/ipa..mjs +73 -0
- package/lib/esm/utils/app-info-parser/resource-finder..mjs +401 -0
- package/lib/esm/utils/app-info-parser/utils..mjs +121 -0
- package/lib/esm/utils/app-info-parser/xml-parser/binary..mjs +569 -0
- package/lib/esm/utils/app-info-parser/xml-parser/manifest..mjs +200 -0
- package/lib/esm/utils/app-info-parser/zip..mjs +65 -0
- package/lib/esm/utils/check-lockfile..mjs +78 -0
- package/lib/esm/utils/check-plugin..mjs +25 -0
- package/lib/esm/utils/constants..mjs +19 -0
- package/lib/esm/utils/dep-versions..mjs +33 -0
- package/lib/esm/utils/git..mjs +43 -0
- package/lib/esm/utils/http-helper..mjs +70 -0
- package/lib/esm/utils/i18n..mjs +23 -0
- package/lib/esm/utils/index..mjs +316 -0
- package/lib/esm/utils/latest-version/cli..mjs +294 -0
- package/lib/esm/utils/latest-version/index..mjs +238 -0
- package/lib/esm/utils/plugin-config..mjs +23 -0
- package/lib/esm/versions..mjs +290 -0
- package/package.json +19 -2
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AabParser } from "./aab";
|
|
2
|
+
import { ApkParser } from "./apk";
|
|
3
|
+
import { AppParser } from "./app";
|
|
4
|
+
import { IpaParser } from "./ipa";
|
|
5
|
+
const supportFileTypes = [
|
|
6
|
+
'ipa',
|
|
7
|
+
'apk',
|
|
8
|
+
'app',
|
|
9
|
+
'aab'
|
|
10
|
+
];
|
|
11
|
+
class AppInfoParser {
|
|
12
|
+
parse() {
|
|
13
|
+
return this.parser.parse();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* parser for parsing .ipa or .apk file
|
|
17
|
+
* @param {String | File} file // file's path in Node, instance of File in Browser
|
|
18
|
+
*/ constructor(file){
|
|
19
|
+
if (!file) {
|
|
20
|
+
throw new Error("Param miss: file(file's path in Node, instance of File in browser).");
|
|
21
|
+
}
|
|
22
|
+
const splits = (typeof file === 'string' ? file : file.name).split('.');
|
|
23
|
+
const fileType = splits[splits.length - 1].toLowerCase();
|
|
24
|
+
if (!supportFileTypes.includes(fileType)) {
|
|
25
|
+
throw new Error('Unsupported file type, only support .ipa, .apk, .app, or .aab file.');
|
|
26
|
+
}
|
|
27
|
+
this.file = file;
|
|
28
|
+
switch(fileType){
|
|
29
|
+
case 'ipa':
|
|
30
|
+
this.parser = new IpaParser(this.file);
|
|
31
|
+
break;
|
|
32
|
+
case 'apk':
|
|
33
|
+
this.parser = new ApkParser(this.file);
|
|
34
|
+
break;
|
|
35
|
+
case 'app':
|
|
36
|
+
this.parser = new AppParser(this.file);
|
|
37
|
+
break;
|
|
38
|
+
case 'aab':
|
|
39
|
+
this.parser = new AabParser(this.file);
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export default AppInfoParser;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const parsePlist = require('plist').parse;
|
|
2
|
+
const parseBplist = require('bplist-parser').parseBuffer;
|
|
3
|
+
const cgbiToPng = require('cgbi-to-png');
|
|
4
|
+
import { findIpaIconPath, getBase64FromBuffer, isBrowser } from "./utils";
|
|
5
|
+
import { Zip } from "./zip";
|
|
6
|
+
const PlistName = /payload\/[^\/]+?.app\/info.plist$/i;
|
|
7
|
+
const ProvisionName = /payload\/.+?\.app\/embedded.mobileprovision/;
|
|
8
|
+
export class IpaParser extends Zip {
|
|
9
|
+
parse() {
|
|
10
|
+
return new Promise((resolve, reject)=>{
|
|
11
|
+
this.getEntries([
|
|
12
|
+
PlistName,
|
|
13
|
+
ProvisionName
|
|
14
|
+
]).then((buffers)=>{
|
|
15
|
+
if (!buffers[PlistName]) {
|
|
16
|
+
throw new Error("Info.plist can't be found.");
|
|
17
|
+
}
|
|
18
|
+
const plistInfo = this._parsePlist(buffers[PlistName]);
|
|
19
|
+
const provisionInfo = this._parseProvision(buffers[ProvisionName]);
|
|
20
|
+
plistInfo.mobileProvision = provisionInfo;
|
|
21
|
+
const iconRegex = new RegExp(findIpaIconPath(plistInfo).toLowerCase());
|
|
22
|
+
this.getEntry(iconRegex).then((iconBuffer)=>{
|
|
23
|
+
try {
|
|
24
|
+
plistInfo.icon = iconBuffer ? getBase64FromBuffer(cgbiToPng.revert(iconBuffer)) : null;
|
|
25
|
+
} catch (err) {
|
|
26
|
+
if (isBrowser()) {
|
|
27
|
+
plistInfo.icon = iconBuffer ? getBase64FromBuffer(window.btoa(String.fromCharCode(...iconBuffer))) : null;
|
|
28
|
+
} else {
|
|
29
|
+
plistInfo.icon = null;
|
|
30
|
+
console.warn('[Warning] failed to parse icon: ', err);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
resolve(plistInfo);
|
|
34
|
+
}).catch((e)=>{
|
|
35
|
+
reject(e);
|
|
36
|
+
});
|
|
37
|
+
}).catch((e)=>{
|
|
38
|
+
reject(e);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Parse plist
|
|
44
|
+
* @param {Buffer} buffer // plist file's buffer
|
|
45
|
+
*/ _parsePlist(buffer) {
|
|
46
|
+
let result;
|
|
47
|
+
const bufferType = buffer[0];
|
|
48
|
+
if (bufferType === 60 || bufferType === '<' || bufferType === 239) {
|
|
49
|
+
result = parsePlist(buffer.toString());
|
|
50
|
+
} else if (bufferType === 98) {
|
|
51
|
+
result = parseBplist(buffer)[0];
|
|
52
|
+
} else {
|
|
53
|
+
throw new Error('Unknown plist buffer type.');
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* parse provision
|
|
59
|
+
* @param {Buffer} buffer // provision file's buffer
|
|
60
|
+
*/ _parseProvision(buffer) {
|
|
61
|
+
let info = {};
|
|
62
|
+
if (buffer) {
|
|
63
|
+
let content = buffer.toString('utf-8');
|
|
64
|
+
const firstIndex = content.indexOf('<?xml');
|
|
65
|
+
const endIndex = content.indexOf('</plist>');
|
|
66
|
+
content = content.slice(firstIndex, endIndex + 8);
|
|
67
|
+
if (content) {
|
|
68
|
+
info = parsePlist(content);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return info;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,401 @@
|
|
|
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
|
+
*/ const ByteBuffer = require('bytebuffer');
|
|
6
|
+
const DEBUG = false;
|
|
7
|
+
const RES_STRING_POOL_TYPE = 0x0001;
|
|
8
|
+
const RES_TABLE_TYPE = 0x0002;
|
|
9
|
+
const RES_TABLE_PACKAGE_TYPE = 0x0200;
|
|
10
|
+
const RES_TABLE_TYPE_TYPE = 0x0201;
|
|
11
|
+
const RES_TABLE_TYPE_SPEC_TYPE = 0x0202;
|
|
12
|
+
const TYPE_REFERENCE = 0x01;
|
|
13
|
+
const TYPE_STRING = 0x03;
|
|
14
|
+
export class ResourceFinder {
|
|
15
|
+
/**
|
|
16
|
+
* Same to C# BinaryReader.readBytes
|
|
17
|
+
*
|
|
18
|
+
* @param bb ByteBuffer
|
|
19
|
+
* @param len length
|
|
20
|
+
* @returns {Buffer}
|
|
21
|
+
*/ static readBytes(bb, len) {
|
|
22
|
+
const uint8Array = new Uint8Array(len);
|
|
23
|
+
for(let i = 0; i < len; i++){
|
|
24
|
+
uint8Array[i] = bb.readUint8();
|
|
25
|
+
}
|
|
26
|
+
return ByteBuffer.wrap(uint8Array, 'binary', true);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
* @param {ByteBuffer} bb
|
|
31
|
+
* @return {Map<String, Set<String>>}
|
|
32
|
+
*/ processResourceTable(resourceBuffer) {
|
|
33
|
+
const bb = ByteBuffer.wrap(resourceBuffer, 'binary', true);
|
|
34
|
+
const type = bb.readShort();
|
|
35
|
+
const headerSize = bb.readShort();
|
|
36
|
+
const size = bb.readInt();
|
|
37
|
+
const packageCount = bb.readInt();
|
|
38
|
+
let buffer;
|
|
39
|
+
let bb2;
|
|
40
|
+
if (type !== RES_TABLE_TYPE) {
|
|
41
|
+
throw new Error('No RES_TABLE_TYPE found!');
|
|
42
|
+
}
|
|
43
|
+
if (size !== bb.limit) {
|
|
44
|
+
throw new Error('The buffer size not matches to the resource table size.');
|
|
45
|
+
}
|
|
46
|
+
bb.offset = headerSize;
|
|
47
|
+
let realStringPoolCount = 0;
|
|
48
|
+
let realPackageCount = 0;
|
|
49
|
+
while(true){
|
|
50
|
+
let pos = 0;
|
|
51
|
+
let t = 0;
|
|
52
|
+
let hs = 0;
|
|
53
|
+
let s = 0;
|
|
54
|
+
try {
|
|
55
|
+
pos = bb.offset;
|
|
56
|
+
t = bb.readShort();
|
|
57
|
+
hs = bb.readShort();
|
|
58
|
+
s = bb.readInt();
|
|
59
|
+
} catch (e) {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
if (t === RES_STRING_POOL_TYPE) {
|
|
63
|
+
if (realStringPoolCount === 0) {
|
|
64
|
+
if (DEBUG) {
|
|
65
|
+
console.log('Processing the string pool ...');
|
|
66
|
+
}
|
|
67
|
+
buffer = new ByteBuffer(s);
|
|
68
|
+
bb.offset = pos;
|
|
69
|
+
bb.prependTo(buffer);
|
|
70
|
+
bb2 = ByteBuffer.wrap(buffer, 'binary', true);
|
|
71
|
+
bb2.LE();
|
|
72
|
+
this.valueStringPool = this.processStringPool(bb2);
|
|
73
|
+
}
|
|
74
|
+
realStringPoolCount++;
|
|
75
|
+
} else if (t === RES_TABLE_PACKAGE_TYPE) {
|
|
76
|
+
if (DEBUG) {
|
|
77
|
+
console.log(`Processing the package ${realPackageCount} ...`);
|
|
78
|
+
}
|
|
79
|
+
buffer = new ByteBuffer(s);
|
|
80
|
+
bb.offset = pos;
|
|
81
|
+
bb.prependTo(buffer);
|
|
82
|
+
bb2 = ByteBuffer.wrap(buffer, 'binary', true);
|
|
83
|
+
bb2.LE();
|
|
84
|
+
this.processPackage(bb2);
|
|
85
|
+
realPackageCount++;
|
|
86
|
+
} else {
|
|
87
|
+
throw new Error('Unsupported type');
|
|
88
|
+
}
|
|
89
|
+
bb.offset = pos + s;
|
|
90
|
+
if (!bb.remaining()) break;
|
|
91
|
+
}
|
|
92
|
+
if (realStringPoolCount !== 1) {
|
|
93
|
+
throw new Error('More than 1 string pool found!');
|
|
94
|
+
}
|
|
95
|
+
if (realPackageCount !== packageCount) {
|
|
96
|
+
throw new Error('Real package count not equals the declared count.');
|
|
97
|
+
}
|
|
98
|
+
return this.responseMap;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
*
|
|
102
|
+
* @param {ByteBuffer} bb
|
|
103
|
+
*/ processPackage(bb) {
|
|
104
|
+
const type = bb.readShort();
|
|
105
|
+
const headerSize = bb.readShort();
|
|
106
|
+
const size = bb.readInt();
|
|
107
|
+
const id = bb.readInt();
|
|
108
|
+
void type;
|
|
109
|
+
void size;
|
|
110
|
+
this.packageId = id;
|
|
111
|
+
for(let i = 0; i < 256; ++i){
|
|
112
|
+
bb.readUint8();
|
|
113
|
+
}
|
|
114
|
+
const typeStrings = bb.readInt();
|
|
115
|
+
const lastPublicType = bb.readInt();
|
|
116
|
+
const keyStrings = bb.readInt();
|
|
117
|
+
const lastPublicKey = bb.readInt();
|
|
118
|
+
void lastPublicType;
|
|
119
|
+
void lastPublicKey;
|
|
120
|
+
if (typeStrings !== headerSize) {
|
|
121
|
+
throw new Error('TypeStrings must immediately following the package structure header.');
|
|
122
|
+
}
|
|
123
|
+
if (DEBUG) {
|
|
124
|
+
console.log('Type strings:');
|
|
125
|
+
}
|
|
126
|
+
let lastPosition = bb.offset;
|
|
127
|
+
bb.offset = typeStrings;
|
|
128
|
+
const bbTypeStrings = ResourceFinder.readBytes(bb, bb.limit - bb.offset);
|
|
129
|
+
bb.offset = lastPosition;
|
|
130
|
+
this.typeStringPool = this.processStringPool(bbTypeStrings);
|
|
131
|
+
if (DEBUG) {
|
|
132
|
+
console.log('Key strings:');
|
|
133
|
+
}
|
|
134
|
+
bb.offset = keyStrings;
|
|
135
|
+
const keyType = bb.readShort();
|
|
136
|
+
const keyHeaderSize = bb.readShort();
|
|
137
|
+
const keySize = bb.readInt();
|
|
138
|
+
void keyType;
|
|
139
|
+
void keyHeaderSize;
|
|
140
|
+
lastPosition = bb.offset;
|
|
141
|
+
bb.offset = keyStrings;
|
|
142
|
+
const bbKeyStrings = ResourceFinder.readBytes(bb, bb.limit - bb.offset);
|
|
143
|
+
bb.offset = lastPosition;
|
|
144
|
+
this.keyStringPool = this.processStringPool(bbKeyStrings);
|
|
145
|
+
let typeSpecCount = 0;
|
|
146
|
+
let typeCount = 0;
|
|
147
|
+
bb.offset = keyStrings + keySize;
|
|
148
|
+
let bb2;
|
|
149
|
+
while(true){
|
|
150
|
+
const pos = bb.offset;
|
|
151
|
+
try {
|
|
152
|
+
const t = bb.readShort();
|
|
153
|
+
const hs = bb.readShort();
|
|
154
|
+
const s = bb.readInt();
|
|
155
|
+
void hs;
|
|
156
|
+
if (t === RES_TABLE_TYPE_SPEC_TYPE) {
|
|
157
|
+
bb.offset = pos;
|
|
158
|
+
bb2 = ResourceFinder.readBytes(bb, s);
|
|
159
|
+
this.processTypeSpec(bb2);
|
|
160
|
+
typeSpecCount++;
|
|
161
|
+
} else if (t === RES_TABLE_TYPE_TYPE) {
|
|
162
|
+
bb.offset = pos;
|
|
163
|
+
bb2 = ResourceFinder.readBytes(bb, s);
|
|
164
|
+
this.processType(bb2);
|
|
165
|
+
typeCount++;
|
|
166
|
+
}
|
|
167
|
+
if (s === 0) {
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
bb.offset = pos + s;
|
|
171
|
+
if (!bb.remaining()) {
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
} catch (e) {
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
void typeSpecCount;
|
|
179
|
+
void typeCount;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
*
|
|
183
|
+
* @param {ByteBuffer} bb
|
|
184
|
+
*/ processType(bb) {
|
|
185
|
+
const type = bb.readShort();
|
|
186
|
+
const headerSize = bb.readShort();
|
|
187
|
+
const size = bb.readInt();
|
|
188
|
+
const id = bb.readByte();
|
|
189
|
+
const res0 = bb.readByte();
|
|
190
|
+
const res1 = bb.readShort();
|
|
191
|
+
const entryCount = bb.readInt();
|
|
192
|
+
const entriesStart = bb.readInt();
|
|
193
|
+
void type;
|
|
194
|
+
void size;
|
|
195
|
+
void res0;
|
|
196
|
+
void res1;
|
|
197
|
+
const refKeys = {};
|
|
198
|
+
const configSize = bb.readInt();
|
|
199
|
+
void configSize;
|
|
200
|
+
bb.offset = headerSize;
|
|
201
|
+
if (headerSize + entryCount * 4 !== entriesStart) {
|
|
202
|
+
throw new Error('HeaderSize, entryCount and entriesStart are not valid.');
|
|
203
|
+
}
|
|
204
|
+
const entryIndices = new Array(entryCount);
|
|
205
|
+
for(let i = 0; i < entryCount; ++i){
|
|
206
|
+
entryIndices[i] = bb.readInt();
|
|
207
|
+
}
|
|
208
|
+
for(let i = 0; i < entryCount; ++i){
|
|
209
|
+
if (entryIndices[i] === -1) continue;
|
|
210
|
+
const resourceId = this.packageId << 24 | id << 16 | i;
|
|
211
|
+
let entrySize = 0;
|
|
212
|
+
let entryFlag = 0;
|
|
213
|
+
let entryKey = 0;
|
|
214
|
+
try {
|
|
215
|
+
entrySize = bb.readShort();
|
|
216
|
+
entryFlag = bb.readShort();
|
|
217
|
+
entryKey = bb.readInt();
|
|
218
|
+
} catch (e) {
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
void entrySize;
|
|
222
|
+
const FLAG_COMPLEX = 0x0001;
|
|
223
|
+
if ((entryFlag & FLAG_COMPLEX) === 0) {
|
|
224
|
+
const valueSize = bb.readShort();
|
|
225
|
+
const valueRes0 = bb.readByte();
|
|
226
|
+
const valueDataType = bb.readByte();
|
|
227
|
+
const valueData = bb.readInt();
|
|
228
|
+
void valueSize;
|
|
229
|
+
void valueRes0;
|
|
230
|
+
const idStr = Number(resourceId).toString(16);
|
|
231
|
+
const keyStr = this.keyStringPool ? this.keyStringPool[entryKey] : '';
|
|
232
|
+
let data = null;
|
|
233
|
+
if (DEBUG) {
|
|
234
|
+
console.log(`Entry 0x${idStr}, key: ${keyStr}, simple value type: `);
|
|
235
|
+
}
|
|
236
|
+
const key = Number.parseInt(idStr, 16);
|
|
237
|
+
var _this_entryMap_key;
|
|
238
|
+
const entryArr = (_this_entryMap_key = this.entryMap[key]) != null ? _this_entryMap_key : [];
|
|
239
|
+
entryArr.push(keyStr);
|
|
240
|
+
this.entryMap[key] = entryArr;
|
|
241
|
+
if (valueDataType === TYPE_STRING) {
|
|
242
|
+
data = this.valueStringPool ? this.valueStringPool[valueData] : null;
|
|
243
|
+
if (DEBUG && this.valueStringPool) {
|
|
244
|
+
console.log(`, data: ${this.valueStringPool[valueData]}`);
|
|
245
|
+
}
|
|
246
|
+
} else if (valueDataType === TYPE_REFERENCE) {
|
|
247
|
+
refKeys[idStr] = valueData;
|
|
248
|
+
} else {
|
|
249
|
+
data = `${valueData}`;
|
|
250
|
+
if (DEBUG) {
|
|
251
|
+
console.log(`, data: ${valueData}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
this.putIntoMap(`@${idStr}`, data);
|
|
255
|
+
} else {
|
|
256
|
+
const entryParent = bb.readInt();
|
|
257
|
+
const entryCountValue = bb.readInt();
|
|
258
|
+
void entryParent;
|
|
259
|
+
for(let j = 0; j < entryCountValue; ++j){
|
|
260
|
+
const refName = bb.readInt();
|
|
261
|
+
const valueSize = bb.readShort();
|
|
262
|
+
const valueRes0 = bb.readByte();
|
|
263
|
+
const valueDataType = bb.readByte();
|
|
264
|
+
const valueData = bb.readInt();
|
|
265
|
+
void refName;
|
|
266
|
+
void valueSize;
|
|
267
|
+
void valueRes0;
|
|
268
|
+
void valueDataType;
|
|
269
|
+
void valueData;
|
|
270
|
+
}
|
|
271
|
+
if (DEBUG) {
|
|
272
|
+
const keyStr = this.keyStringPool ? this.keyStringPool[entryKey] : '';
|
|
273
|
+
console.log(`Entry 0x${Number(resourceId).toString(16)}, key: ${keyStr}, complex value, not printed.`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
for(const refKey in refKeys){
|
|
278
|
+
const values = this.responseMap[`@${Number(refKeys[refKey]).toString(16).toUpperCase()}`];
|
|
279
|
+
if (values != null && Object.keys(values).length < 1000) {
|
|
280
|
+
for (const value of values){
|
|
281
|
+
this.putIntoMap(`@${refKey}`, value);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
*
|
|
288
|
+
* @param {ByteBuffer} bb
|
|
289
|
+
* @return {Array}
|
|
290
|
+
*/ processStringPool(bb) {
|
|
291
|
+
const type = bb.readShort();
|
|
292
|
+
const headerSize = bb.readShort();
|
|
293
|
+
const size = bb.readInt();
|
|
294
|
+
const stringCount = bb.readInt();
|
|
295
|
+
const styleCount = bb.readInt();
|
|
296
|
+
const flags = bb.readInt();
|
|
297
|
+
const stringsStart = bb.readInt();
|
|
298
|
+
const stylesStart = bb.readInt();
|
|
299
|
+
void type;
|
|
300
|
+
void headerSize;
|
|
301
|
+
void size;
|
|
302
|
+
void styleCount;
|
|
303
|
+
void stylesStart;
|
|
304
|
+
const isUtf8 = (flags & 256) !== 0;
|
|
305
|
+
const offsets = new Array(stringCount);
|
|
306
|
+
for(let i = 0; i < stringCount; ++i){
|
|
307
|
+
offsets[i] = bb.readInt();
|
|
308
|
+
}
|
|
309
|
+
const strings = new Array(stringCount);
|
|
310
|
+
for(let i = 0; i < stringCount; ++i){
|
|
311
|
+
const pos = stringsStart + offsets[i];
|
|
312
|
+
bb.offset = pos;
|
|
313
|
+
strings[i] = '';
|
|
314
|
+
if (isUtf8) {
|
|
315
|
+
let u16len = bb.readUint8();
|
|
316
|
+
if ((u16len & 0x80) !== 0) {
|
|
317
|
+
u16len = ((u16len & 0x7f) << 8) + bb.readUint8();
|
|
318
|
+
}
|
|
319
|
+
let u8len = bb.readUint8();
|
|
320
|
+
if ((u8len & 0x80) !== 0) {
|
|
321
|
+
u8len = ((u8len & 0x7f) << 8) + bb.readUint8();
|
|
322
|
+
}
|
|
323
|
+
if (u8len > 0) {
|
|
324
|
+
const buffer = ResourceFinder.readBytes(bb, u8len);
|
|
325
|
+
try {
|
|
326
|
+
strings[i] = ByteBuffer.wrap(buffer, 'utf8', true).toString('utf8');
|
|
327
|
+
} catch (e) {
|
|
328
|
+
if (DEBUG) {
|
|
329
|
+
console.error(e);
|
|
330
|
+
console.log('Error when turning buffer to utf-8 string.');
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
strings[i] = '';
|
|
335
|
+
}
|
|
336
|
+
} else {
|
|
337
|
+
let u16len = bb.readUint16();
|
|
338
|
+
if ((u16len & 0x8000) !== 0) {
|
|
339
|
+
u16len = ((u16len & 0x7fff) << 16) + bb.readUint16();
|
|
340
|
+
}
|
|
341
|
+
if (u16len > 0) {
|
|
342
|
+
const len = u16len * 2;
|
|
343
|
+
const buffer = ResourceFinder.readBytes(bb, len);
|
|
344
|
+
try {
|
|
345
|
+
strings[i] = ByteBuffer.wrap(buffer, 'utf8', true).toString('utf8');
|
|
346
|
+
} catch (e) {
|
|
347
|
+
if (DEBUG) {
|
|
348
|
+
console.error(e);
|
|
349
|
+
console.log('Error when turning buffer to utf-8 string.');
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (DEBUG) {
|
|
355
|
+
console.log('Parsed value: {0}', strings[i]);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return strings;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
*
|
|
362
|
+
* @param {ByteBuffer} bb
|
|
363
|
+
*/ processTypeSpec(bb) {
|
|
364
|
+
const type = bb.readShort();
|
|
365
|
+
const headerSize = bb.readShort();
|
|
366
|
+
const size = bb.readInt();
|
|
367
|
+
const id = bb.readByte();
|
|
368
|
+
const res0 = bb.readByte();
|
|
369
|
+
const res1 = bb.readShort();
|
|
370
|
+
const entryCount = bb.readInt();
|
|
371
|
+
void type;
|
|
372
|
+
void headerSize;
|
|
373
|
+
void size;
|
|
374
|
+
void res0;
|
|
375
|
+
void res1;
|
|
376
|
+
if (DEBUG && this.typeStringPool) {
|
|
377
|
+
console.log(`Processing type spec ${this.typeStringPool[id - 1]}...`);
|
|
378
|
+
}
|
|
379
|
+
const flags = new Array(entryCount);
|
|
380
|
+
for(let i = 0; i < entryCount; ++i){
|
|
381
|
+
flags[i] = bb.readInt();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
putIntoMap(resId, value) {
|
|
385
|
+
const key = resId.toUpperCase();
|
|
386
|
+
if (this.responseMap[key] == null) {
|
|
387
|
+
this.responseMap[key] = [];
|
|
388
|
+
}
|
|
389
|
+
if (value) {
|
|
390
|
+
this.responseMap[key].push(value);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
constructor(){
|
|
394
|
+
this.valueStringPool = null;
|
|
395
|
+
this.typeStringPool = null;
|
|
396
|
+
this.keyStringPool = null;
|
|
397
|
+
this.packageId = 0;
|
|
398
|
+
this.responseMap = {};
|
|
399
|
+
this.entryMap = {};
|
|
400
|
+
}
|
|
401
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const objectType = (value)=>Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
|
|
2
|
+
const isArray = (value)=>objectType(value) === 'array';
|
|
3
|
+
const isObject = (value)=>objectType(value) === 'object';
|
|
4
|
+
const isPrimitive = (value)=>value === null || [
|
|
5
|
+
'boolean',
|
|
6
|
+
'number',
|
|
7
|
+
'string',
|
|
8
|
+
'undefined'
|
|
9
|
+
].includes(objectType(value));
|
|
10
|
+
const isBrowser = ()=>typeof process === 'undefined' || Object.prototype.toString.call(process) !== '[object process]';
|
|
11
|
+
/**
|
|
12
|
+
* map file place with resourceMap
|
|
13
|
+
* @param {Object} apkInfo // json info parsed from .apk file
|
|
14
|
+
* @param {Object} resourceMap // resourceMap
|
|
15
|
+
*/ const mapInfoResource = (apkInfo, resourceMap)=>{
|
|
16
|
+
const iteratorObj = (obj)=>{
|
|
17
|
+
for(const i in obj){
|
|
18
|
+
if (isArray(obj[i])) {
|
|
19
|
+
iteratorArray(obj[i]);
|
|
20
|
+
} else if (isObject(obj[i])) {
|
|
21
|
+
iteratorObj(obj[i]);
|
|
22
|
+
} else if (isPrimitive(obj[i])) {
|
|
23
|
+
if (isResources(obj[i])) {
|
|
24
|
+
obj[i] = resourceMap[transKeyToMatchResourceMap(obj[i])];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const iteratorArray = (array)=>{
|
|
30
|
+
const l = array.length;
|
|
31
|
+
for(let i = 0; i < l; i++){
|
|
32
|
+
if (isArray(array[i])) {
|
|
33
|
+
iteratorArray(array[i]);
|
|
34
|
+
} else if (isObject(array[i])) {
|
|
35
|
+
iteratorObj(array[i]);
|
|
36
|
+
} else if (isPrimitive(array[i])) {
|
|
37
|
+
if (isResources(array[i])) {
|
|
38
|
+
array[i] = resourceMap[transKeyToMatchResourceMap(array[i])];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const isResources = (attrValue)=>{
|
|
44
|
+
if (!attrValue) return false;
|
|
45
|
+
const value = typeof attrValue === 'string' ? attrValue : attrValue.toString();
|
|
46
|
+
return value.indexOf('resourceId:') === 0;
|
|
47
|
+
};
|
|
48
|
+
const transKeyToMatchResourceMap = (resourceId)=>`@${resourceId.replace('resourceId:0x', '').toUpperCase()}`;
|
|
49
|
+
iteratorObj(apkInfo);
|
|
50
|
+
return apkInfo;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* find .apk file's icon path from json info
|
|
54
|
+
* @param info // json info parsed from .apk file
|
|
55
|
+
*/ const findApkIconPath = (info)=>{
|
|
56
|
+
if (!info.application.icon || !info.application.icon.splice) {
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
const rulesMap = {
|
|
60
|
+
mdpi: 48,
|
|
61
|
+
hdpi: 72,
|
|
62
|
+
xhdpi: 96,
|
|
63
|
+
xxdpi: 144,
|
|
64
|
+
xxxhdpi: 192
|
|
65
|
+
};
|
|
66
|
+
const resultMap = {};
|
|
67
|
+
const maxDpiIcon = {
|
|
68
|
+
dpi: 120,
|
|
69
|
+
icon: ''
|
|
70
|
+
};
|
|
71
|
+
for(const i in rulesMap){
|
|
72
|
+
info.application.icon.some((icon)=>{
|
|
73
|
+
if (icon && icon.indexOf(i) !== -1) {
|
|
74
|
+
resultMap[`application-icon-${rulesMap[i]}`] = icon;
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
});
|
|
79
|
+
if (resultMap[`application-icon-${rulesMap[i]}`] && rulesMap[i] >= maxDpiIcon.dpi) {
|
|
80
|
+
maxDpiIcon.dpi = rulesMap[i];
|
|
81
|
+
maxDpiIcon.icon = resultMap[`application-icon-${rulesMap[i]}`];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (Object.keys(resultMap).length === 0 || !maxDpiIcon.icon) {
|
|
85
|
+
maxDpiIcon.dpi = 120;
|
|
86
|
+
maxDpiIcon.icon = info.application.icon[0] || '';
|
|
87
|
+
resultMap['applicataion-icon-120'] = maxDpiIcon.icon;
|
|
88
|
+
}
|
|
89
|
+
return maxDpiIcon.icon;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* find .ipa file's icon path from json info
|
|
93
|
+
* @param info // json info parsed from .ipa file
|
|
94
|
+
*/ const findIpaIconPath = (info)=>{
|
|
95
|
+
var _info_CFBundleIcons_CFBundlePrimaryIcon_CFBundleIconFiles, _info_CFBundleIcons_CFBundlePrimaryIcon, _info_CFBundleIcons, _info_CFBundleIconFiles;
|
|
96
|
+
if ((_info_CFBundleIcons = info.CFBundleIcons) == null ? void 0 : (_info_CFBundleIcons_CFBundlePrimaryIcon = _info_CFBundleIcons.CFBundlePrimaryIcon) == null ? void 0 : (_info_CFBundleIcons_CFBundlePrimaryIcon_CFBundleIconFiles = _info_CFBundleIcons_CFBundlePrimaryIcon.CFBundleIconFiles) == null ? void 0 : _info_CFBundleIcons_CFBundlePrimaryIcon_CFBundleIconFiles.length) {
|
|
97
|
+
return info.CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles[info.CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles.length - 1];
|
|
98
|
+
}
|
|
99
|
+
if ((_info_CFBundleIconFiles = info.CFBundleIconFiles) == null ? void 0 : _info_CFBundleIconFiles.length) {
|
|
100
|
+
return info.CFBundleIconFiles[info.CFBundleIconFiles.length - 1];
|
|
101
|
+
}
|
|
102
|
+
return '.app/Icon.png';
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* transform buffer to base64
|
|
106
|
+
* @param {Buffer} buffer
|
|
107
|
+
*/ const getBase64FromBuffer = (buffer)=>{
|
|
108
|
+
const base64 = typeof buffer === 'string' ? buffer : buffer.toString('base64');
|
|
109
|
+
return `data:image/png;base64,${base64}`;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* 去除unicode空字符
|
|
113
|
+
* @param {String} str
|
|
114
|
+
*/ const decodeNullUnicode = (value)=>{
|
|
115
|
+
if (typeof value === 'string') {
|
|
116
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: <explanation>
|
|
117
|
+
return value.replace(/\u0000/g, '');
|
|
118
|
+
}
|
|
119
|
+
return value;
|
|
120
|
+
};
|
|
121
|
+
export { isArray, isObject, isPrimitive, isBrowser, mapInfoResource, findApkIconPath, findIpaIconPath, getBase64FromBuffer, decodeNullUnicode };
|