roxify 1.10.0 → 1.10.1
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/Cargo.toml +14 -2
- package/README.md +15 -0
- package/dist/cli.js +8 -1
- package/dist/utils/constants.d.ts +5 -0
- package/dist/utils/constants.js +1 -0
- package/dist/utils/decoder.js +40 -3
- package/dist/utils/encoder.js +24 -11
- package/dist/utils/inspection.d.ts +0 -28
- package/dist/utils/inspection.js +124 -406
- package/dist/utils/native.js +1 -9
- package/dist/utils/rust-cli-wrapper.js +41 -38
- package/dist/utils/types.d.ts +1 -1
- package/libroxify_native-x86_64-unknown-linux-gnu.node +0 -0
- package/native/bench_hybrid.rs +145 -0
- package/native/bwt.rs +25 -69
- package/native/hybrid.rs +92 -70
- package/native/lib.rs +6 -3
- package/native/mtf.rs +106 -0
- package/native/rans_byte.rs +190 -0
- package/native/test_small_bwt.rs +31 -0
- package/native/test_stages.rs +70 -0
- package/package.json +1 -1
- package/dist/rox-macos-universal +0 -0
- package/dist/roxify_native +0 -0
- package/dist/roxify_native-macos-arm64 +0 -0
- package/dist/roxify_native-macos-x64 +0 -0
- package/dist/roxify_native.exe +0 -0
- package/roxify_native-aarch64-apple-darwin.node +0 -0
- package/roxify_native-aarch64-pc-windows-msvc.node +0 -0
- package/roxify_native-aarch64-unknown-linux-gnu.node +0 -0
- package/roxify_native-i686-pc-windows-msvc.node +0 -0
- package/roxify_native-i686-unknown-linux-gnu.node +0 -0
- package/roxify_native-x86_64-apple-darwin.node +0 -0
- package/roxify_native-x86_64-pc-windows-gnu.node +0 -0
- package/roxify_native-x86_64-pc-windows-msvc.node +0 -0
- package/roxify_native-x86_64-unknown-linux-gnu.node +0 -0
package/dist/utils/inspection.js
CHANGED
|
@@ -1,75 +1,47 @@
|
|
|
1
1
|
import * as zlib from 'zlib';
|
|
2
|
-
import {
|
|
3
|
-
import { CHUNK_TYPE, ENC_AES, ENC_XOR, MAGIC, MARKER_COLORS, PIXEL_MAGIC, } from './constants.js';
|
|
4
|
-
import { decodePngToBinary } from './decoder.js';
|
|
5
|
-
import { PassphraseRequiredError } from './errors.js';
|
|
2
|
+
import { CHUNK_TYPE, ENC_AES, ENC_XOR, MAGIC, PIXEL_MAGIC, } from './constants.js';
|
|
6
3
|
import { native } from './native.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
4
|
+
function parseFileList(parsedFiles) {
|
|
5
|
+
if (parsedFiles.length > 0 &&
|
|
6
|
+
typeof parsedFiles[0] === 'object' &&
|
|
7
|
+
(parsedFiles[0].name || parsedFiles[0].path)) {
|
|
8
|
+
return parsedFiles
|
|
9
|
+
.map((p) => ({ name: p.name ?? p.path, size: typeof p.size === 'number' ? p.size : 0 }))
|
|
10
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
11
|
+
}
|
|
12
|
+
return parsedFiles.sort();
|
|
13
|
+
}
|
|
14
|
+
function tryExtractFileListFromChunks(chunks) {
|
|
15
|
+
const rxfl = chunks.find((c) => c.name === 'rXFL');
|
|
16
|
+
if (rxfl) {
|
|
17
|
+
return parseFileList(JSON.parse(Buffer.from(rxfl.data).toString('utf8')));
|
|
18
|
+
}
|
|
19
|
+
const meta = chunks.find((c) => c.name === CHUNK_TYPE);
|
|
20
|
+
if (meta) {
|
|
21
|
+
const dataBuf = Buffer.from(meta.data);
|
|
22
|
+
const markerIdx = dataBuf.indexOf(Buffer.from('rXFL'));
|
|
23
|
+
if (markerIdx !== -1 && markerIdx + 8 <= dataBuf.length) {
|
|
24
|
+
const jsonLen = dataBuf.readUInt32BE(markerIdx + 4);
|
|
25
|
+
const jsonEnd = markerIdx + 8 + jsonLen;
|
|
26
|
+
if (jsonEnd <= dataBuf.length) {
|
|
27
|
+
return parseFileList(JSON.parse(dataBuf.slice(markerIdx + 8, jsonEnd).toString('utf8')));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
23
33
|
export async function listFilesInPng(pngBuf, opts = {}) {
|
|
24
34
|
try {
|
|
25
35
|
const chunks = native.extractPngChunks(pngBuf);
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
const parsedFiles = JSON.parse(data.toString('utf8'));
|
|
30
|
-
if (parsedFiles.length > 0 &&
|
|
31
|
-
typeof parsedFiles[0] === 'object' &&
|
|
32
|
-
(parsedFiles[0].name || parsedFiles[0].path)) {
|
|
33
|
-
const objs = parsedFiles.map((p) => ({
|
|
34
|
-
name: p.name ?? p.path,
|
|
35
|
-
size: typeof p.size === 'number' ? p.size : 0,
|
|
36
|
-
}));
|
|
37
|
-
return objs.sort((a, b) => a.name.localeCompare(b.name));
|
|
38
|
-
}
|
|
39
|
-
const files = parsedFiles;
|
|
40
|
-
return files.sort();
|
|
41
|
-
}
|
|
42
|
-
const metaChunk = chunks.find((c) => c.name === CHUNK_TYPE);
|
|
43
|
-
if (metaChunk) {
|
|
44
|
-
const dataBuf = Buffer.from(metaChunk.data);
|
|
45
|
-
const markerIdx = dataBuf.indexOf(Buffer.from('rXFL'));
|
|
46
|
-
if (markerIdx !== -1 && markerIdx + 8 <= dataBuf.length) {
|
|
47
|
-
const jsonLen = dataBuf.readUInt32BE(markerIdx + 4);
|
|
48
|
-
const jsonStart = markerIdx + 8;
|
|
49
|
-
const jsonEnd = jsonStart + jsonLen;
|
|
50
|
-
if (jsonEnd <= dataBuf.length) {
|
|
51
|
-
const parsedFiles = JSON.parse(dataBuf.slice(jsonStart, jsonEnd).toString('utf8'));
|
|
52
|
-
if (parsedFiles.length > 0 &&
|
|
53
|
-
typeof parsedFiles[0] === 'object' &&
|
|
54
|
-
(parsedFiles[0].name || parsedFiles[0].path)) {
|
|
55
|
-
const objs = parsedFiles.map((p) => ({
|
|
56
|
-
name: p.name ?? p.path,
|
|
57
|
-
size: typeof p.size === 'number' ? p.size : 0,
|
|
58
|
-
}));
|
|
59
|
-
return objs.sort((a, b) => a.name.localeCompare(b.name));
|
|
60
|
-
}
|
|
61
|
-
const files = parsedFiles;
|
|
62
|
-
return files.sort();
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
36
|
+
const result = tryExtractFileListFromChunks(chunks);
|
|
37
|
+
if (result)
|
|
38
|
+
return result;
|
|
66
39
|
const ihdr = chunks.find((c) => c.name === 'IHDR');
|
|
67
40
|
const idatChunks = chunks.filter((c) => c.name === 'IDAT');
|
|
68
41
|
if (ihdr && idatChunks.length > 0) {
|
|
69
42
|
const ihdrData = Buffer.from(ihdr.data);
|
|
70
43
|
const width = ihdrData.readUInt32BE(0);
|
|
71
|
-
const
|
|
72
|
-
const rowLen = 1 + width * bpp;
|
|
44
|
+
const rowLen = 1 + width * 3;
|
|
73
45
|
const files = await new Promise((resolve) => {
|
|
74
46
|
const inflate = zlib.createInflate();
|
|
75
47
|
let buffer = Buffer.alloc(0);
|
|
@@ -98,8 +70,7 @@ export async function listFilesInPng(pngBuf, opts = {}) {
|
|
|
98
70
|
const validClean = cleanBuffer.slice(0, cleanPtr);
|
|
99
71
|
if (validClean.length < 12)
|
|
100
72
|
return;
|
|
101
|
-
|
|
102
|
-
if (!magic.equals(PIXEL_MAGIC)) {
|
|
73
|
+
if (!validClean.slice(8, 12).equals(PIXEL_MAGIC)) {
|
|
103
74
|
resolved = true;
|
|
104
75
|
inflate.destroy();
|
|
105
76
|
resolve(null);
|
|
@@ -116,8 +87,7 @@ export async function listFilesInPng(pngBuf, opts = {}) {
|
|
|
116
87
|
idx += 4;
|
|
117
88
|
if (validClean.length < idx + 4)
|
|
118
89
|
return;
|
|
119
|
-
|
|
120
|
-
if (marker === 'rXFL') {
|
|
90
|
+
if (validClean.slice(idx, idx + 4).toString('utf8') === 'rXFL') {
|
|
121
91
|
idx += 4;
|
|
122
92
|
if (validClean.length < idx + 4)
|
|
123
93
|
return;
|
|
@@ -125,27 +95,12 @@ export async function listFilesInPng(pngBuf, opts = {}) {
|
|
|
125
95
|
idx += 4;
|
|
126
96
|
if (validClean.length < idx + jsonLen)
|
|
127
97
|
return;
|
|
128
|
-
const jsonBuf = validClean.slice(idx, idx + jsonLen);
|
|
129
98
|
try {
|
|
130
|
-
const parsedFiles = JSON.parse(jsonBuf.toString('utf8'));
|
|
131
99
|
resolved = true;
|
|
132
100
|
inflate.destroy();
|
|
133
|
-
|
|
134
|
-
typeof parsedFiles[0] === 'object' &&
|
|
135
|
-
(parsedFiles[0].name || parsedFiles[0].path)) {
|
|
136
|
-
const objs = parsedFiles.map((p) => ({
|
|
137
|
-
name: p.name ?? p.path,
|
|
138
|
-
size: typeof p.size === 'number' ? p.size : 0,
|
|
139
|
-
}));
|
|
140
|
-
resolve(objs.sort((a, b) => a.name.localeCompare(b.name)));
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
const names = parsedFiles;
|
|
144
|
-
resolve(names.sort());
|
|
101
|
+
resolve(parseFileList(JSON.parse(validClean.slice(idx, idx + jsonLen).toString('utf8'))));
|
|
145
102
|
}
|
|
146
|
-
catch
|
|
147
|
-
resolved = true;
|
|
148
|
-
inflate.destroy();
|
|
103
|
+
catch {
|
|
149
104
|
resolve(null);
|
|
150
105
|
}
|
|
151
106
|
}
|
|
@@ -155,14 +110,10 @@ export async function listFilesInPng(pngBuf, opts = {}) {
|
|
|
155
110
|
resolve(null);
|
|
156
111
|
}
|
|
157
112
|
});
|
|
158
|
-
inflate.on('error', () => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
inflate.on('end', () => {
|
|
163
|
-
if (!resolved)
|
|
164
|
-
resolve(null);
|
|
165
|
-
});
|
|
113
|
+
inflate.on('error', () => { if (!resolved)
|
|
114
|
+
resolve(null); });
|
|
115
|
+
inflate.on('end', () => { if (!resolved)
|
|
116
|
+
resolve(null); });
|
|
166
117
|
for (const chunk of idatChunks) {
|
|
167
118
|
if (resolved)
|
|
168
119
|
break;
|
|
@@ -174,265 +125,16 @@ export async function listFilesInPng(pngBuf, opts = {}) {
|
|
|
174
125
|
return files;
|
|
175
126
|
}
|
|
176
127
|
}
|
|
177
|
-
catch (e) {
|
|
178
|
-
console.log(' error:', e);
|
|
179
|
-
}
|
|
180
|
-
try {
|
|
181
|
-
try {
|
|
182
|
-
const rawData = native.sharpToRaw(pngBuf);
|
|
183
|
-
const data = rawData.pixels;
|
|
184
|
-
const currentWidth = rawData.width;
|
|
185
|
-
const currentHeight = rawData.height;
|
|
186
|
-
const rawRGB = Buffer.alloc(currentWidth * currentHeight * 3);
|
|
187
|
-
for (let i = 0; i < currentWidth * currentHeight; i++) {
|
|
188
|
-
rawRGB[i * 3] = data[i * 3];
|
|
189
|
-
rawRGB[i * 3 + 1] = data[i * 4 + 1];
|
|
190
|
-
rawRGB[i * 3 + 2] = data[i * 4 + 2];
|
|
191
|
-
}
|
|
192
|
-
const found = rawRGB.indexOf(PIXEL_MAGIC);
|
|
193
|
-
if (found !== -1) {
|
|
194
|
-
let idx = found + PIXEL_MAGIC.length;
|
|
195
|
-
if (idx + 2 <= rawRGB.length) {
|
|
196
|
-
const version = rawRGB[idx++];
|
|
197
|
-
const nameLen = rawRGB[idx++];
|
|
198
|
-
if (process.env.ROX_DEBUG)
|
|
199
|
-
console.log('listFilesInPng: pixel version', version, 'nameLen', nameLen);
|
|
200
|
-
if (nameLen > 0 && idx + nameLen <= rawRGB.length) {
|
|
201
|
-
idx += nameLen;
|
|
202
|
-
}
|
|
203
|
-
if (idx + 4 <= rawRGB.length) {
|
|
204
|
-
const payloadLen = rawRGB.readUInt32BE(idx);
|
|
205
|
-
idx += 4;
|
|
206
|
-
const afterPayload = idx + payloadLen;
|
|
207
|
-
if (afterPayload <= rawRGB.length) {
|
|
208
|
-
if (afterPayload + 8 <= rawRGB.length) {
|
|
209
|
-
const marker = rawRGB
|
|
210
|
-
.slice(afterPayload, afterPayload + 4)
|
|
211
|
-
.toString('utf8');
|
|
212
|
-
if (marker === 'rXFL') {
|
|
213
|
-
const jsonLen = rawRGB.readUInt32BE(afterPayload + 4);
|
|
214
|
-
const jsonStart = afterPayload + 8;
|
|
215
|
-
const jsonEnd = jsonStart + jsonLen;
|
|
216
|
-
if (jsonEnd <= rawRGB.length) {
|
|
217
|
-
const jsonBuf = rawRGB.slice(jsonStart, jsonEnd);
|
|
218
|
-
const parsedFiles = JSON.parse(jsonBuf.toString('utf8'));
|
|
219
|
-
if (parsedFiles.length > 0 &&
|
|
220
|
-
typeof parsedFiles[0] === 'object' &&
|
|
221
|
-
(parsedFiles[0].name || parsedFiles[0].path)) {
|
|
222
|
-
const objs = parsedFiles.map((p) => ({
|
|
223
|
-
name: p.name ?? p.path,
|
|
224
|
-
size: typeof p.size === 'number' ? p.size : 0,
|
|
225
|
-
}));
|
|
226
|
-
return objs.sort((a, b) => a.name.localeCompare(b.name));
|
|
227
|
-
}
|
|
228
|
-
const files = parsedFiles;
|
|
229
|
-
return files.sort();
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
catch (e) { }
|
|
239
|
-
}
|
|
240
|
-
catch (e) { }
|
|
241
|
-
try {
|
|
242
|
-
const reconstructed = await cropAndReconstitute(pngBuf);
|
|
243
|
-
try {
|
|
244
|
-
const rawData = native.sharpToRaw(reconstructed);
|
|
245
|
-
const data = rawData.pixels;
|
|
246
|
-
const currentWidth = rawData.width;
|
|
247
|
-
const currentHeight = rawData.height;
|
|
248
|
-
const rawRGB = Buffer.alloc(currentWidth * currentHeight * 3);
|
|
249
|
-
for (let i = 0; i < currentWidth * currentHeight; i++) {
|
|
250
|
-
rawRGB[i * 3] = data[i * 3];
|
|
251
|
-
rawRGB[i * 3 + 1] = data[i * 3 + 1];
|
|
252
|
-
rawRGB[i * 3 + 2] = data[i * 3 + 2];
|
|
253
|
-
}
|
|
254
|
-
const found = rawRGB.indexOf(PIXEL_MAGIC);
|
|
255
|
-
if (found !== -1) {
|
|
256
|
-
let idx = found + PIXEL_MAGIC.length;
|
|
257
|
-
if (idx + 2 <= rawRGB.length) {
|
|
258
|
-
const version = rawRGB[idx++];
|
|
259
|
-
const nameLen = rawRGB[idx++];
|
|
260
|
-
if (process.env.ROX_DEBUG)
|
|
261
|
-
console.log('listFilesInPng (reconstructed): pixel version', version, 'nameLen', nameLen);
|
|
262
|
-
if (nameLen > 0 && idx + nameLen <= rawRGB.length) {
|
|
263
|
-
idx += nameLen;
|
|
264
|
-
}
|
|
265
|
-
if (idx + 4 <= rawRGB.length) {
|
|
266
|
-
const payloadLen = rawRGB.readUInt32BE(idx);
|
|
267
|
-
idx += 4;
|
|
268
|
-
const afterPayload = idx + payloadLen;
|
|
269
|
-
if (afterPayload <= rawRGB.length) {
|
|
270
|
-
if (afterPayload + 8 <= rawRGB.length) {
|
|
271
|
-
const marker = rawRGB
|
|
272
|
-
.slice(afterPayload, afterPayload + 4)
|
|
273
|
-
.toString('utf8');
|
|
274
|
-
if (marker === 'rXFL') {
|
|
275
|
-
const jsonLen = rawRGB.readUInt32BE(afterPayload + 4);
|
|
276
|
-
const jsonStart = afterPayload + 8;
|
|
277
|
-
const jsonEnd = jsonStart + jsonLen;
|
|
278
|
-
if (jsonEnd <= rawRGB.length) {
|
|
279
|
-
const jsonBuf = rawRGB.slice(jsonStart, jsonEnd);
|
|
280
|
-
const parsedFiles = JSON.parse(jsonBuf.toString('utf8'));
|
|
281
|
-
if (parsedFiles.length > 0 &&
|
|
282
|
-
typeof parsedFiles[0] === 'object' &&
|
|
283
|
-
(parsedFiles[0].name || parsedFiles[0].path)) {
|
|
284
|
-
const objs = parsedFiles.map((p) => ({
|
|
285
|
-
name: p.name ?? p.path,
|
|
286
|
-
size: typeof p.size === 'number' ? p.size : 0,
|
|
287
|
-
}));
|
|
288
|
-
return objs.sort((a, b) => a.name.localeCompare(b.name));
|
|
289
|
-
}
|
|
290
|
-
const files = parsedFiles;
|
|
291
|
-
return files.sort();
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
catch (e) { }
|
|
301
|
-
try {
|
|
302
|
-
const chunks = native.extractPngChunks(reconstructed);
|
|
303
|
-
const fileListChunk = chunks.find((c) => c.name === 'rXFL');
|
|
304
|
-
if (fileListChunk) {
|
|
305
|
-
const data = Buffer.from(fileListChunk.data);
|
|
306
|
-
const parsedFiles = JSON.parse(data.toString('utf8'));
|
|
307
|
-
if (parsedFiles.length > 0 &&
|
|
308
|
-
typeof parsedFiles[0] === 'object' &&
|
|
309
|
-
(parsedFiles[0].name || parsedFiles[0].path)) {
|
|
310
|
-
const objs = parsedFiles.map((p) => ({
|
|
311
|
-
name: p.name ?? p.path,
|
|
312
|
-
size: typeof p.size === 'number' ? p.size : 0,
|
|
313
|
-
}));
|
|
314
|
-
return objs.sort((a, b) => a.name.localeCompare(b.name));
|
|
315
|
-
}
|
|
316
|
-
const files = parsedFiles;
|
|
317
|
-
if (opts.includeSizes) {
|
|
318
|
-
const sizes = await getFileSizesFromPng(pngBuf);
|
|
319
|
-
if (sizes) {
|
|
320
|
-
return files
|
|
321
|
-
.map((f) => ({ name: f, size: sizes[f] ?? 0 }))
|
|
322
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return files.sort();
|
|
326
|
-
}
|
|
327
|
-
const metaChunk = chunks.find((c) => c.name === CHUNK_TYPE);
|
|
328
|
-
if (metaChunk) {
|
|
329
|
-
const dataBuf = Buffer.from(metaChunk.data);
|
|
330
|
-
const markerIdx = dataBuf.indexOf(Buffer.from('rXFL'));
|
|
331
|
-
if (markerIdx !== -1 && markerIdx + 8 <= dataBuf.length) {
|
|
332
|
-
const jsonLen = dataBuf.readUInt32BE(markerIdx + 4);
|
|
333
|
-
const jsonStart = markerIdx + 8;
|
|
334
|
-
const jsonEnd = jsonStart + jsonLen;
|
|
335
|
-
if (jsonEnd <= dataBuf.length) {
|
|
336
|
-
const parsedFiles = JSON.parse(dataBuf.slice(jsonStart, jsonEnd).toString('utf8'));
|
|
337
|
-
if (parsedFiles.length > 0 &&
|
|
338
|
-
typeof parsedFiles[0] === 'object' &&
|
|
339
|
-
(parsedFiles[0].name || parsedFiles[0].path)) {
|
|
340
|
-
const objs = parsedFiles.map((p) => ({
|
|
341
|
-
name: p.name ?? p.path,
|
|
342
|
-
size: typeof p.size === 'number' ? p.size : 0,
|
|
343
|
-
}));
|
|
344
|
-
return objs.sort((a, b) => a.name.localeCompare(b.name));
|
|
345
|
-
}
|
|
346
|
-
const files = parsedFiles;
|
|
347
|
-
return files.sort();
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
catch (e) { }
|
|
353
|
-
}
|
|
354
|
-
catch (e) { }
|
|
355
|
-
try {
|
|
356
|
-
const chunks = native.extractPngChunks(pngBuf);
|
|
357
|
-
const fileListChunk = chunks.find((c) => c.name === 'rXFL');
|
|
358
|
-
if (fileListChunk) {
|
|
359
|
-
const data = Buffer.from(fileListChunk.data);
|
|
360
|
-
const parsedFiles = JSON.parse(data.toString('utf8'));
|
|
361
|
-
if (parsedFiles.length > 0 &&
|
|
362
|
-
typeof parsedFiles[0] === 'object' &&
|
|
363
|
-
(parsedFiles[0].name || parsedFiles[0].path)) {
|
|
364
|
-
const objs = parsedFiles.map((p) => ({
|
|
365
|
-
name: p.name ?? p.path,
|
|
366
|
-
size: typeof p.size === 'number' ? p.size : 0,
|
|
367
|
-
}));
|
|
368
|
-
return objs.sort((a, b) => a.name.localeCompare(b.name));
|
|
369
|
-
}
|
|
370
|
-
const files = parsedFiles;
|
|
371
|
-
return files.sort();
|
|
372
|
-
}
|
|
373
|
-
const metaChunk = chunks.find((c) => c.name === CHUNK_TYPE);
|
|
374
|
-
if (metaChunk) {
|
|
375
|
-
const dataBuf = Buffer.from(metaChunk.data);
|
|
376
|
-
const markerIdx = dataBuf.indexOf(Buffer.from('rXFL'));
|
|
377
|
-
if (markerIdx !== -1 && markerIdx + 8 <= dataBuf.length) {
|
|
378
|
-
const jsonLen = dataBuf.readUInt32BE(markerIdx + 4);
|
|
379
|
-
const jsonStart = markerIdx + 8;
|
|
380
|
-
const jsonEnd = jsonStart + jsonLen;
|
|
381
|
-
if (jsonEnd <= dataBuf.length) {
|
|
382
|
-
const parsedFiles = JSON.parse(dataBuf.slice(jsonStart, jsonEnd).toString('utf8'));
|
|
383
|
-
if (parsedFiles.length > 0 &&
|
|
384
|
-
typeof parsedFiles[0] === 'object' &&
|
|
385
|
-
(parsedFiles[0].name || parsedFiles[0].path)) {
|
|
386
|
-
const objs = parsedFiles.map((p) => ({
|
|
387
|
-
name: p.name ?? p.path,
|
|
388
|
-
size: typeof p.size === 'number' ? p.size : 0,
|
|
389
|
-
}));
|
|
390
|
-
return objs.sort((a, b) => a.name.localeCompare(b.name));
|
|
391
|
-
}
|
|
392
|
-
const files = parsedFiles;
|
|
393
|
-
return files.sort();
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
128
|
catch (e) { }
|
|
399
|
-
return null;
|
|
400
|
-
}
|
|
401
|
-
async function getFileSizesFromPng(pngBuf) {
|
|
402
129
|
try {
|
|
403
|
-
const
|
|
404
|
-
if (
|
|
405
|
-
|
|
406
|
-
for (const f of res.files)
|
|
407
|
-
map[f.path] = f.buf.length;
|
|
408
|
-
return map;
|
|
409
|
-
}
|
|
410
|
-
if (res && res.buf) {
|
|
411
|
-
const unpack = unpackBuffer(res.buf);
|
|
412
|
-
if (unpack) {
|
|
413
|
-
const map = {};
|
|
414
|
-
for (const f of unpack.files)
|
|
415
|
-
map[f.path] = f.buf.length;
|
|
416
|
-
return map;
|
|
417
|
-
}
|
|
130
|
+
const json = native.extractFileListFromPixels(pngBuf);
|
|
131
|
+
if (json) {
|
|
132
|
+
return parseFileList(JSON.parse(json));
|
|
418
133
|
}
|
|
419
134
|
}
|
|
420
135
|
catch (e) { }
|
|
421
136
|
return null;
|
|
422
137
|
}
|
|
423
|
-
/**
|
|
424
|
-
* Check if a PNG contains an encrypted payload requiring a passphrase.
|
|
425
|
-
*
|
|
426
|
-
* @param pngBuf - Buffer containing a PNG file.
|
|
427
|
-
* @returns Promise resolving to `true` if the PNG requires a passphrase.
|
|
428
|
-
*
|
|
429
|
-
* @example
|
|
430
|
-
* ```js
|
|
431
|
-
* import { hasPassphraseInPng } from 'roxify';
|
|
432
|
-
* const needPass = await hasPassphraseInPng(fs.readFileSync('out.png'));
|
|
433
|
-
* console.log('needs passphrase?', needPass);
|
|
434
|
-
* ```
|
|
435
|
-
*/
|
|
436
138
|
export async function hasPassphraseInPng(pngBuf) {
|
|
437
139
|
try {
|
|
438
140
|
if (pngBuf.slice(0, MAGIC.length).equals(MAGIC)) {
|
|
@@ -446,74 +148,90 @@ export async function hasPassphraseInPng(pngBuf) {
|
|
|
446
148
|
const flag = pngBuf[offset];
|
|
447
149
|
return flag === ENC_AES || flag === ENC_XOR;
|
|
448
150
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
if (data.length
|
|
457
|
-
|
|
458
|
-
const payloadStart = 1 + nameLen;
|
|
459
|
-
if (payloadStart < data.length) {
|
|
460
|
-
const flag = data[payloadStart];
|
|
461
|
-
return flag === ENC_AES || flag === ENC_XOR;
|
|
462
|
-
}
|
|
151
|
+
const chunks = native.extractPngChunks(pngBuf);
|
|
152
|
+
const target = chunks.find((c) => c.name === CHUNK_TYPE);
|
|
153
|
+
if (target) {
|
|
154
|
+
const data = Buffer.isBuffer(target.data) ? target.data : Buffer.from(target.data);
|
|
155
|
+
if (data.length >= 1) {
|
|
156
|
+
const nameLen = data.readUInt8(0);
|
|
157
|
+
const payloadStart = 1 + nameLen;
|
|
158
|
+
if (payloadStart < data.length) {
|
|
159
|
+
return data[payloadStart] === ENC_AES || data[payloadStart] === ENC_XOR;
|
|
463
160
|
}
|
|
464
161
|
}
|
|
465
162
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
163
|
+
const ihdr = chunks.find((c) => c.name === 'IHDR');
|
|
164
|
+
const idatChunks = chunks.filter((c) => c.name === 'IDAT');
|
|
165
|
+
if (ihdr && idatChunks.length > 0) {
|
|
166
|
+
const ihdrData = Buffer.from(ihdr.data);
|
|
167
|
+
const width = ihdrData.readUInt32BE(0);
|
|
168
|
+
const rowLen = 1 + width * 3;
|
|
169
|
+
return await new Promise((resolve) => {
|
|
170
|
+
const inflate = zlib.createInflate();
|
|
171
|
+
let buffer = Buffer.alloc(0);
|
|
172
|
+
let resolved = false;
|
|
173
|
+
inflate.on('data', (chunk) => {
|
|
174
|
+
if (resolved)
|
|
175
|
+
return;
|
|
176
|
+
buffer = Buffer.concat([buffer, chunk]);
|
|
177
|
+
const cleanBuffer = Buffer.alloc(buffer.length);
|
|
178
|
+
let cleanPtr = 0;
|
|
179
|
+
let ptr = 0;
|
|
180
|
+
while (ptr < buffer.length) {
|
|
181
|
+
const rowPos = ptr % rowLen;
|
|
182
|
+
if (rowPos === 0) {
|
|
183
|
+
ptr++;
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const rem = rowLen - rowPos;
|
|
187
|
+
const avail = buffer.length - ptr;
|
|
188
|
+
const toCopy = Math.min(rem, avail);
|
|
189
|
+
buffer.copy(cleanBuffer, cleanPtr, ptr, ptr + toCopy);
|
|
190
|
+
cleanPtr += toCopy;
|
|
191
|
+
ptr += toCopy;
|
|
192
|
+
}
|
|
480
193
|
}
|
|
194
|
+
const valid = cleanBuffer.slice(0, cleanPtr);
|
|
195
|
+
if (valid.length < 12)
|
|
196
|
+
return;
|
|
197
|
+
if (!valid.slice(8, 12).equals(PIXEL_MAGIC)) {
|
|
198
|
+
resolved = true;
|
|
199
|
+
inflate.destroy();
|
|
200
|
+
resolve(false);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
let idx = 12;
|
|
204
|
+
if (valid.length < idx + 2)
|
|
205
|
+
return;
|
|
206
|
+
idx++;
|
|
207
|
+
const nameLen = valid[idx++];
|
|
208
|
+
if (valid.length < idx + nameLen + 4)
|
|
209
|
+
return;
|
|
210
|
+
idx += nameLen;
|
|
211
|
+
if (valid.length < idx + 4 + 1)
|
|
212
|
+
return;
|
|
213
|
+
const payloadLen = valid.readUInt32BE(idx);
|
|
214
|
+
idx += 4;
|
|
215
|
+
if (valid.length < idx + 1)
|
|
216
|
+
return;
|
|
217
|
+
const flag = valid[idx];
|
|
218
|
+
resolved = true;
|
|
219
|
+
inflate.destroy();
|
|
220
|
+
resolve(flag === ENC_AES || flag === ENC_XOR);
|
|
221
|
+
});
|
|
222
|
+
inflate.on('error', () => { if (!resolved)
|
|
223
|
+
resolve(false); });
|
|
224
|
+
inflate.on('end', () => { if (!resolved)
|
|
225
|
+
resolve(false); });
|
|
226
|
+
for (const chunk of idatChunks) {
|
|
227
|
+
if (resolved)
|
|
228
|
+
break;
|
|
229
|
+
inflate.write(Buffer.from(chunk.data));
|
|
481
230
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const headerStart = i + markerLen;
|
|
485
|
-
if (headerStart + PIXEL_MAGIC.length >= rawRGB.length)
|
|
486
|
-
continue;
|
|
487
|
-
if (!rawRGB
|
|
488
|
-
.slice(headerStart, headerStart + PIXEL_MAGIC.length)
|
|
489
|
-
.equals(PIXEL_MAGIC))
|
|
490
|
-
continue;
|
|
491
|
-
const metaStart = headerStart + PIXEL_MAGIC.length;
|
|
492
|
-
if (metaStart + 2 >= rawRGB.length)
|
|
493
|
-
continue;
|
|
494
|
-
const nameLen = rawRGB[metaStart + 1];
|
|
495
|
-
const payloadLenOff = metaStart + 2 + nameLen;
|
|
496
|
-
const payloadStart = payloadLenOff + 4;
|
|
497
|
-
if (payloadStart >= rawRGB.length)
|
|
498
|
-
continue;
|
|
499
|
-
const flag = rawRGB[payloadStart];
|
|
500
|
-
return flag === ENC_AES || flag === ENC_XOR;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
catch (e) { }
|
|
504
|
-
try {
|
|
505
|
-
await decodePngToBinary(pngBuf, { showProgress: false });
|
|
506
|
-
return false;
|
|
507
|
-
}
|
|
508
|
-
catch (e) {
|
|
509
|
-
if (e instanceof PassphraseRequiredError)
|
|
510
|
-
return true;
|
|
511
|
-
if (e.message && e.message.toLowerCase().includes('passphrase'))
|
|
512
|
-
return true;
|
|
513
|
-
return false;
|
|
231
|
+
inflate.end();
|
|
232
|
+
});
|
|
514
233
|
}
|
|
515
234
|
}
|
|
516
|
-
catch (e) {
|
|
517
|
-
|
|
518
|
-
}
|
|
235
|
+
catch (e) { }
|
|
236
|
+
return false;
|
|
519
237
|
}
|
package/dist/utils/native.js
CHANGED
|
@@ -72,15 +72,7 @@ function getNativeModule() {
|
|
|
72
72
|
for (const triple of triples) {
|
|
73
73
|
const name = `roxify_native-${triple}.node`;
|
|
74
74
|
const libName = `libroxify_native-${triple}.node`;
|
|
75
|
-
candidates.push(
|
|
76
|
-
// dist/ sibling (npm-installed package)
|
|
77
|
-
resolve(moduleDir, '..', name), resolve(moduleDir, '..', libName),
|
|
78
|
-
// package root
|
|
79
|
-
resolve(root, name), resolve(root, libName),
|
|
80
|
-
// node_modules/roxify/
|
|
81
|
-
resolve(root, 'node_modules', 'roxify', name), resolve(root, 'node_modules', 'roxify', libName),
|
|
82
|
-
// two levels up (global npm install)
|
|
83
|
-
resolve(moduleDir, '..', '..', name), resolve(moduleDir, '..', '..', libName));
|
|
75
|
+
candidates.push(resolve(moduleDir, '..', name), resolve(moduleDir, '..', libName), resolve(root, name), resolve(root, libName), resolve(root, 'node_modules', 'roxify', name), resolve(root, 'node_modules', 'roxify', libName), resolve(moduleDir, '..', '..', name), resolve(moduleDir, '..', '..', libName));
|
|
84
76
|
}
|
|
85
77
|
// --- 2. Build output candidates (local dev) ---
|
|
86
78
|
for (const triple of triples) {
|