roxify 1.13.7 → 1.13.9

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.
Files changed (56) hide show
  1. package/dist/cli.js +1 -8
  2. package/dist/stub-progress.d.ts +4 -4
  3. package/dist/stub-progress.js +4 -4
  4. package/dist/utils/decoder.d.ts +46 -2
  5. package/dist/utils/decoder.js +248 -38
  6. package/dist/utils/ecc.js +0 -1
  7. package/dist/utils/encoder.d.ts +30 -1
  8. package/dist/utils/encoder.js +34 -18
  9. package/dist/utils/inspection.d.ts +1 -1
  10. package/dist/utils/inspection.js +2 -2
  11. package/dist/utils/robust-audio.js +0 -13
  12. package/dist/utils/robust-image.js +0 -26
  13. package/package.json +12 -29
  14. package/roxify_native-aarch64-apple-darwin.node +0 -0
  15. package/roxify_native-aarch64-pc-windows-msvc.node +0 -0
  16. package/roxify_native-aarch64-unknown-linux-gnu.node +0 -0
  17. package/roxify_native-i686-pc-windows-msvc.node +0 -0
  18. package/roxify_native-i686-unknown-linux-gnu.node +0 -0
  19. package/{dist/rox-macos-universal → roxify_native-universal-apple-darwin.node} +0 -0
  20. package/roxify_native-x86_64-apple-darwin.node +0 -0
  21. package/roxify_native-x86_64-pc-windows-msvc.node +0 -0
  22. package/roxify_native-x86_64-unknown-linux-gnu.node +0 -0
  23. package/scripts/postinstall.cjs +23 -2
  24. package/Cargo.toml +0 -91
  25. package/dist/roxify_native +0 -0
  26. package/dist/roxify_native-macos-arm64 +0 -0
  27. package/dist/roxify_native-macos-x64 +0 -0
  28. package/dist/roxify_native.exe +0 -0
  29. package/native/archive.rs +0 -220
  30. package/native/audio.rs +0 -151
  31. package/native/bench_hybrid.rs +0 -145
  32. package/native/bwt.rs +0 -56
  33. package/native/context_mixing.rs +0 -117
  34. package/native/core.rs +0 -378
  35. package/native/crypto.rs +0 -209
  36. package/native/encoder.rs +0 -405
  37. package/native/hybrid.rs +0 -297
  38. package/native/image_utils.rs +0 -82
  39. package/native/io_advice.rs +0 -43
  40. package/native/io_ntfs_optimized.rs +0 -99
  41. package/native/lib.rs +0 -480
  42. package/native/main.rs +0 -842
  43. package/native/mtf.rs +0 -106
  44. package/native/packer.rs +0 -604
  45. package/native/png_chunk_writer.rs +0 -146
  46. package/native/png_utils.rs +0 -554
  47. package/native/pool.rs +0 -101
  48. package/native/progress.rs +0 -142
  49. package/native/rans.rs +0 -149
  50. package/native/rans_byte.rs +0 -286
  51. package/native/reconstitution.rs +0 -623
  52. package/native/streaming.rs +0 -189
  53. package/native/streaming_decode.rs +0 -625
  54. package/native/streaming_encode.rs +0 -684
  55. package/native/test_small_bwt.rs +0 -31
  56. package/native/test_stages.rs +0 -70
package/dist/cli.js CHANGED
@@ -20,7 +20,7 @@ async function loadJsEngine() {
20
20
  VFSIndexEntry: undefined,
21
21
  };
22
22
  }
23
- const VERSION = '1.13.6';
23
+ const VERSION = '1.13.9';
24
24
  function getDirectorySize(dirPath) {
25
25
  let totalSize = 0;
26
26
  try {
@@ -315,13 +315,6 @@ async function encodeCommand(args) {
315
315
  }
316
316
  }
317
317
  catch (e) { }
318
- let anyInputDir = false;
319
- try {
320
- anyInputDir = resolvedInputs.some((p) => statSync(p).isDirectory());
321
- }
322
- catch (e) {
323
- anyInputDir = false;
324
- }
325
318
  if (isRustBinaryAvailable() && !parsed.forceTs && containerMode !== 'sound' && parsed.compression !== 'bwt-ans') {
326
319
  try {
327
320
  console.log(`Encoding to ${resolvedOutput} (Using native Rust encoder)\n`);
@@ -1,8 +1,8 @@
1
1
  export declare class SingleBar {
2
- constructor(...args: any[]);
3
- start(...args: any[]): void;
4
- update(...args: any[]): void;
5
- stop(...args: any[]): void;
2
+ constructor(..._args: any[]);
3
+ start(..._args: any[]): void;
4
+ update(..._args: any[]): void;
5
+ stop(..._args: any[]): void;
6
6
  }
7
7
  export declare const Presets: {
8
8
  shades_classic: {};
@@ -1,8 +1,8 @@
1
1
  export class SingleBar {
2
- constructor(...args) { }
3
- start(...args) { }
4
- update(...args) { }
5
- stop(...args) { }
2
+ constructor(..._args) { }
3
+ start(..._args) { }
4
+ update(..._args) { }
5
+ stop(..._args) { }
6
6
  }
7
7
  export const Presets = {
8
8
  shades_classic: {},
@@ -1,10 +1,54 @@
1
1
  import { DecodeOptions, DecodeResult } from './types.js';
2
2
  /**
3
3
  * Decode a ROX PNG or buffer into the original binary payload or files list.
4
- * This function uses the Rust native implementation exclusively.
4
+ * This function extracts pixels, parses the payload header, handles encryption/decryption,
5
+ * decompresses with zstd, and returns the decoded buffer - all in memory.
6
+ *
7
+ * @example
8
+ * ```js
9
+ * import { readFileSync, writeFileSync } from 'fs';
10
+ * import { decodePngToBinary } from 'roxify';
11
+ *
12
+ * // Decode a PNG file
13
+ * const png = readFileSync('config.png');
14
+ * const result = await decodePngToBinary(png);
15
+ *
16
+ * console.log(result.buf.toString()); // Original content
17
+ * console.log(result.meta?.name); // Original filename (e.g., "config.json")
18
+ *
19
+ * // Save with original filename
20
+ * writeFileSync(result.meta?.name || 'output.bin', result.buf);
21
+ * ```
22
+ *
23
+ * @example
24
+ * ```js
25
+ * // Decode from file path
26
+ * const result = await decodePngToBinary('config.png');
27
+ * writeFileSync('output.bin', result.buf);
28
+ * ```
29
+ *
30
+ * @example
31
+ * ```js
32
+ * // Handle multi-file archives
33
+ * const result = await decodePngToBinary(png);
34
+ * if (result.files) {
35
+ * for (const file of result.files) {
36
+ * writeFileSync(file.path, file.buf);
37
+ * }
38
+ * }
39
+ * ```
5
40
  *
6
41
  * @param input - Buffer or path to a PNG file.
7
42
  * @param opts - Optional decode options.
8
43
  * @returns A Promise resolving to DecodeResult ({ buf, meta } or { files }).
9
44
  */
10
- export declare function decodePngToBinary(input: Buffer | string, opts?: DecodeOptions): Promise<DecodeResult>;
45
+ export declare function decodePngToBinary(input: Buffer | string, _opts?: DecodeOptions): Promise<DecodeResult>;
46
+ /**
47
+ * Detect and reverse simple pixel-stretching where each logical pixel
48
+ * is repeated in an Fx×Fy block. Returns { width, height, data } or null.
49
+ */
50
+ export declare function unstretchImage(pixels: Buffer, width: number, height: number): {
51
+ width: number;
52
+ height: number;
53
+ data: Buffer<ArrayBuffer>;
54
+ } | null;
@@ -1,15 +1,122 @@
1
1
  import { readFileSync } from 'fs';
2
2
  import { native } from './native.js';
3
3
  import { unpackBuffer } from '../pack.js';
4
+ /**
5
+ * Find PXL1 magic in pixel buffer
6
+ */
7
+ function findPxl1Offset(pixels) {
8
+ for (let i = 0; i <= pixels.length - 4; i++) {
9
+ if (pixels[i] === 0x50 && pixels[i + 1] === 0x58 &&
10
+ pixels[i + 2] === 0x4c && pixels[i + 3] === 0x31) {
11
+ return i;
12
+ }
13
+ }
14
+ return -1;
15
+ }
16
+ /**
17
+ * Parse payload header and extract compressed data from pixels
18
+ */
19
+ function extractPayloadFromPixels(pixels) {
20
+ const pos = findPxl1Offset(pixels);
21
+ if (pos < 0) {
22
+ throw new Error('PXL1 magic not found in pixels');
23
+ }
24
+ let offset = pos + 4; // Skip "PXL1"
25
+ // Read version (1 byte)
26
+ if (offset >= pixels.length) {
27
+ throw new Error('Truncated header: missing version');
28
+ }
29
+ const version = pixels[offset];
30
+ offset += 1;
31
+ // Read name length (1 byte)
32
+ if (offset >= pixels.length) {
33
+ throw new Error('Truncated header: missing name length');
34
+ }
35
+ const nameLen = pixels[offset];
36
+ offset += 1;
37
+ // Read name if present
38
+ let name;
39
+ if (nameLen > 0) {
40
+ if (offset + nameLen > pixels.length) {
41
+ throw new Error('Truncated header: name exceeds buffer');
42
+ }
43
+ name = pixels.subarray(offset, offset + nameLen).toString('utf8');
44
+ offset += nameLen;
45
+ }
46
+ // Read payload length
47
+ if (version === 1) {
48
+ if (offset + 4 > pixels.length) {
49
+ throw new Error('Truncated header: missing payload length (V1)');
50
+ }
51
+ const payloadLen = pixels.readUInt32BE(offset);
52
+ offset += 4;
53
+ if (offset + payloadLen > pixels.length) {
54
+ throw new Error('Truncated payload data');
55
+ }
56
+ const payload = pixels.subarray(offset, offset + payloadLen);
57
+ return { payload, name };
58
+ }
59
+ else if (version === 2) {
60
+ if (offset + 8 > pixels.length) {
61
+ throw new Error('Truncated header: missing payload length (V2)');
62
+ }
63
+ const payloadLen = Number(pixels.readBigUInt64BE(offset));
64
+ offset += 8;
65
+ if (offset + payloadLen > pixels.length) {
66
+ throw new Error('Truncated payload data');
67
+ }
68
+ const payload = pixels.subarray(offset, offset + payloadLen);
69
+ return { payload, name };
70
+ }
71
+ else {
72
+ throw new Error(`Unsupported header version: ${version}`);
73
+ }
74
+ }
4
75
  /**
5
76
  * Decode a ROX PNG or buffer into the original binary payload or files list.
6
- * This function uses the Rust native implementation exclusively.
77
+ * This function extracts pixels, parses the payload header, handles encryption/decryption,
78
+ * decompresses with zstd, and returns the decoded buffer - all in memory.
79
+ *
80
+ * @example
81
+ * ```js
82
+ * import { readFileSync, writeFileSync } from 'fs';
83
+ * import { decodePngToBinary } from 'roxify';
84
+ *
85
+ * // Decode a PNG file
86
+ * const png = readFileSync('config.png');
87
+ * const result = await decodePngToBinary(png);
88
+ *
89
+ * console.log(result.buf.toString()); // Original content
90
+ * console.log(result.meta?.name); // Original filename (e.g., "config.json")
91
+ *
92
+ * // Save with original filename
93
+ * writeFileSync(result.meta?.name || 'output.bin', result.buf);
94
+ * ```
95
+ *
96
+ * @example
97
+ * ```js
98
+ * // Decode from file path
99
+ * const result = await decodePngToBinary('config.png');
100
+ * writeFileSync('output.bin', result.buf);
101
+ * ```
102
+ *
103
+ * @example
104
+ * ```js
105
+ * // Handle multi-file archives
106
+ * const result = await decodePngToBinary(png);
107
+ * if (result.files) {
108
+ * for (const file of result.files) {
109
+ * writeFileSync(file.path, file.buf);
110
+ * }
111
+ * }
112
+ * ```
7
113
  *
8
114
  * @param input - Buffer or path to a PNG file.
9
115
  * @param opts - Optional decode options.
10
116
  * @returns A Promise resolving to DecodeResult ({ buf, meta } or { files }).
11
117
  */
12
- export async function decodePngToBinary(input, opts = {}) {
118
+ export async function decodePngToBinary(input, _opts = {}) {
119
+ // Get PNG buffer
13
120
  let pngBuf;
14
121
  if (Buffer.isBuffer(input)) {
15
122
  pngBuf = input;
@@ -17,51 +124,154 @@ export async function decodePngToBinary(input, opts = {}) {
17
124
  else {
18
125
  pngBuf = readFileSync(input);
19
126
  }
20
- // --- Native decoder: let Rust handle extraction/decompression/decryption ---
21
127
  const payload = Buffer.from(native.extractPayloadFromPng(pngBuf));
128
+ let name;
129
+ try {
130
+ const rgbResult = native.pngToRgb(pngBuf);
131
+ const pixels = Buffer.from(rgbResult.pixels);
132
+ ({ name } = extractPayloadFromPixels(pixels));
133
+ }
134
+ catch { }
22
135
  if (payload.length === 0) {
23
- throw new Error('No payload found in PNG');
136
+ throw new Error('Empty payload extracted');
24
137
  }
25
- // Extract name from payload header (version byte, name length, name)
26
- let name;
27
- let dataOffset = 0;
28
- if (payload.length > 2) {
29
- const version = payload[0];
30
- const nameLen = payload[1];
31
- dataOffset = 2;
32
- if (nameLen > 0 && payload.length >= 2 + nameLen + 8) {
33
- name = payload.subarray(2, 2 + nameLen).toString('utf8');
34
- dataOffset = 2 + nameLen;
138
+ // Handle encryption flag (first byte)
139
+ // 0x00 = none, 0x01 = XOR, 0x02 = AES, 0x03 = AES-CTR
140
+ let data;
141
+ if (payload[0] !== 0x00) {
142
+ // Encrypted payload - not supported in current decoder
143
+ // The native encoder handles encryption, but decoder needs native decrypt support
144
+ throw new Error('Encrypted payload requires passphrase (not yet implemented in decoder)');
145
+ }
146
+ else {
147
+ // Non-encrypted: skip the flag byte
148
+ data = payload.subarray(1);
149
+ }
150
+ // Decompress with zstd
151
+ let decompressed;
152
+ try {
153
+ decompressed = Buffer.from(native.nativeZstdDecompress(data));
154
+ }
155
+ catch (e) {
156
+ // If decompression fails, try using raw data (might be uncompressed)
157
+ decompressed = data;
158
+ }
159
+ // Remove ROX1 prefix if present
160
+ if (decompressed.length >= 4 && decompressed.subarray(0, 4).toString() === 'ROX1') {
161
+ decompressed = decompressed.subarray(4);
162
+ }
163
+ // Try to unpack as multi-file archive
164
+ try {
165
+ const unpacked = unpackBuffer(decompressed);
166
+ if (unpacked && unpacked.files && unpacked.files.length > 0) {
167
+ // Return files directly as PackedFile[]
168
+ return { files: unpacked.files, meta: { name } };
35
169
  }
36
- // Read payload length (8 bytes, big-endian, after name)
37
- const payloadLen = Number(payload.readBigUInt64BE(dataOffset));
38
- dataOffset += 8;
39
- // The actual compressed/encrypted data starts at dataOffset
40
- let compressedData = payload.subarray(dataOffset, dataOffset + payloadLen);
41
- // Try to decompress with zstd if needed
42
- let decompressed;
43
- try {
44
- decompressed = Buffer.from(native.nativeZstdDecompress(compressedData));
170
+ }
171
+ catch {
172
+ // Not a multi-file archive, return as single buffer
173
+ }
174
+ return { buf: decompressed, meta: { name } };
175
+ }
176
+ /**
177
+ * Detect and reverse simple pixel-stretching where each logical pixel
178
+ * is repeated in an Fx×Fy block. Returns { width, height, data } or null.
179
+ */
180
+ export function unstretchImage(pixels, width, height) {
181
+ if (!Buffer.isBuffer(pixels))
182
+ return null;
183
+ if (pixels.length !== width * height * 3)
184
+ return null;
185
+ try {
186
+ let allWhite = true;
187
+ for (let i = 0; i < pixels.length; i += 3) {
188
+ if (!(pixels[i] === 255 && pixels[i + 1] === 255 && pixels[i + 2] === 255)) {
189
+ allWhite = false;
190
+ break;
191
+ }
45
192
  }
46
- catch {
47
- // If decompression fails, use raw data
48
- decompressed = compressedData;
193
+ if (allWhite)
194
+ return null;
195
+ let minX = width, minY = height, maxX = -1, maxY = -1;
196
+ for (let y = 0; y < height; y++) {
197
+ for (let x = 0; x < width; x++) {
198
+ const idx = (y * width + x) * 3;
199
+ if (!(pixels[idx] === 255 && pixels[idx + 1] === 255 && pixels[idx + 2] === 255)) {
200
+ if (x < minX)
201
+ minX = x;
202
+ if (y < minY)
203
+ minY = y;
204
+ if (x > maxX)
205
+ maxX = x;
206
+ if (y > maxY)
207
+ maxY = y;
208
+ }
209
+ }
210
+ }
211
+ if (maxX < 0)
212
+ return null;
213
+ const cropW = maxX - minX + 1;
214
+ const cropH = maxY - minY + 1;
215
+ const colSig = [];
216
+ for (let x = 0; x < cropW; x++) {
217
+ const parts = [];
218
+ for (let y = 0; y < cropH; y++) {
219
+ const idx = ((minY + y) * width + (minX + x)) * 3;
220
+ parts.push(pixels[idx], pixels[idx + 1], pixels[idx + 2]);
221
+ }
222
+ colSig.push(parts.join(','));
49
223
  }
50
- // Check for ROX1 magic
51
- if (decompressed.length >= 4 && decompressed.subarray(0, 4).toString() === 'ROX1') {
52
- decompressed = decompressed.subarray(4);
224
+ const colGroups = [];
225
+ for (let x = 0; x < colSig.length; x++) {
226
+ if (x === 0 || colSig[x] !== colSig[x - 1])
227
+ colGroups.push({ start: x, len: 1 });
228
+ else
229
+ colGroups[colGroups.length - 1].len++;
53
230
  }
54
- // Try to unpack as multi-file archive
55
- try {
56
- const unpacked = unpackBuffer(decompressed);
57
- if (unpacked && unpacked.files && unpacked.files.length > 0) {
58
- return { files: unpacked.files, meta: { name } };
231
+ const rowSig = [];
232
+ for (let y = 0; y < cropH; y++) {
233
+ const parts = [];
234
+ for (let x = 0; x < cropW; x++) {
235
+ const idx = ((minY + y) * width + (minX + x)) * 3;
236
+ parts.push(pixels[idx], pixels[idx + 1], pixels[idx + 2]);
59
237
  }
238
+ rowSig.push(parts.join(','));
60
239
  }
61
- catch {
62
- // Fall through to raw buffer return
240
+ const rowGroups = [];
241
+ for (let y = 0; y < rowSig.length; y++) {
242
+ if (y === 0 || rowSig[y] !== rowSig[y - 1])
243
+ rowGroups.push({ start: y, len: 1 });
244
+ else
245
+ rowGroups[rowGroups.length - 1].len++;
63
246
  }
64
- return { buf: decompressed, meta: { name } };
247
+ const outW = colGroups.length;
248
+ const outH = rowGroups.length;
249
+ const hasRun = colGroups.some(g => g.len > 1) || rowGroups.some(g => g.len > 1);
250
+ if (!hasRun)
251
+ return null;
252
+ const out = Buffer.alloc(outW * outH * 3);
253
+ for (let gy = 0; gy < outH; gy++) {
254
+ for (let gx = 0; gx < outW; gx++) {
255
+ const sx = minX + colGroups[gx].start;
256
+ const sy = minY + rowGroups[gy].start;
257
+ const baseIdx = (sy * width + sx) * 3;
258
+ const r = pixels[baseIdx], g = pixels[baseIdx + 1], b = pixels[baseIdx + 2];
259
+ for (let ry = 0; ry < rowGroups[gy].len; ry++) {
260
+ for (let rx = 0; rx < colGroups[gx].len; rx++) {
261
+ const ix = ((sy + ry) * width + (sx + rx)) * 3;
262
+ if (pixels[ix] !== r || pixels[ix + 1] !== g || pixels[ix + 2] !== b)
263
+ return null;
264
+ }
265
+ }
266
+ const outIdx = (gy * outW + gx) * 3;
267
+ out[outIdx] = r;
268
+ out[outIdx + 1] = g;
269
+ out[outIdx + 2] = b;
270
+ }
271
+ }
272
+ return { width: outW, height: outH, data: out };
273
+ }
274
+ catch {
275
+ return null;
65
276
  }
66
- return { buf: payload, meta: { name } };
67
277
  }
package/dist/utils/ecc.js CHANGED
@@ -429,7 +429,6 @@ export function eccDecode(protected_) {
429
429
  // RS-decode each block
430
430
  let totalCorrected = 0;
431
431
  const decoded = [];
432
- const k = dataPerBlock(nsym);
433
432
  for (let i = 0; i < numBlocks; i++) {
434
433
  const { data, corrected } = rsDecode(blocks[i], nsym);
435
434
  totalCorrected += corrected;
@@ -1,7 +1,36 @@
1
1
  import { EncodeOptions } from './types.js';
2
2
  /**
3
3
  * Encode a buffer or array of buffers into a PNG image (ROX format).
4
- * This function uses the Rust native implementation exclusively.
4
+ * This function uses the native Rust encoder directly.
5
+ *
6
+ * @example
7
+ * ```js
8
+ * import { readFileSync, writeFileSync } from 'fs';
9
+ * import { encodeBinaryToPng } from 'roxify';
10
+ *
11
+ * // Encode a file with a custom filename
12
+ * const input = readFileSync('config.json');
13
+ * const png = await encodeBinaryToPng(input, { name: 'config.json' });
14
+ * writeFileSync('config.png', png);
15
+ * ```
16
+ *
17
+ * @example
18
+ * ```js
19
+ * // Encode without filename
20
+ * const input = Buffer.from('Hello World');
21
+ * const png = await encodeBinaryToPng(input);
22
+ * ```
23
+ *
24
+ * @example
25
+ * ```js
26
+ * // Encode with encryption (AES)
27
+ * const input = readFileSync('secret.txt');
28
+ * const png = await encodeBinaryToPng(input, {
29
+ * name: 'secret.txt',
30
+ * passphrase: 'my-secret-key',
31
+ * encrypt: 'aes'
32
+ * });
33
+ * ```
5
34
  *
6
35
  * @param input - The buffer or array of buffers to encode.
7
36
  * @param opts - Optional encoding options.
@@ -1,31 +1,44 @@
1
1
  import { native } from './native.js';
2
- function normalizeNativeFileList(fileList) {
3
- if (!fileList)
4
- return '[]';
5
- return JSON.stringify(fileList.map((entry) => {
6
- if (typeof entry === 'string') {
7
- return { name: entry, size: 0 };
8
- }
9
- if (entry && typeof entry === 'object') {
10
- if (entry.name)
11
- return { name: entry.name, size: entry.size ?? 0 };
12
- if (entry.path)
13
- return { name: entry.path, size: entry.size ?? 0 };
14
- }
15
- return { name: String(entry), size: 0 };
16
- }));
17
- }
18
2
  /**
19
3
  * Encode a buffer or array of buffers into a PNG image (ROX format).
20
- * This function uses the Rust native implementation exclusively.
4
+ * This function uses the native Rust encoder directly.
5
+ *
6
+ * @example
7
+ * ```js
8
+ * import { readFileSync, writeFileSync } from 'fs';
9
+ * import { encodeBinaryToPng } from 'roxify';
10
+ *
11
+ * // Encode a file with a custom filename
12
+ * const input = readFileSync('config.json');
13
+ * const png = await encodeBinaryToPng(input, { name: 'config.json' });
14
+ * writeFileSync('config.png', png);
15
+ * ```
16
+ *
17
+ * @example
18
+ * ```js
19
+ * // Encode without filename
20
+ * const input = Buffer.from('Hello World');
21
+ * const png = await encodeBinaryToPng(input);
22
+ * ```
23
+ *
24
+ * @example
25
+ * ```js
26
+ * // Encode with encryption (AES)
27
+ * const input = readFileSync('secret.txt');
28
+ * const png = await encodeBinaryToPng(input, {
29
+ * name: 'secret.txt',
30
+ * passphrase: 'my-secret-key',
31
+ * encrypt: 'aes'
32
+ * });
33
+ * ```
21
34
  *
22
35
  * @param input - The buffer or array of buffers to encode.
23
36
  * @param opts - Optional encoding options.
24
37
  * @returns A Promise that resolves to a PNG Buffer containing the encoded data.
25
38
  */
26
39
  export async function encodeBinaryToPng(input, opts = {}) {
27
- const compressionLevel = opts.compressionLevel ?? 19;
28
40
  const inputBuf = Array.isArray(input) ? Buffer.concat(input) : input;
41
+ const compressionLevel = opts.compressionLevel ?? 3;
29
42
  const fileName = opts.name || undefined;
30
43
  const fileListJson = opts.includeFileList && opts.fileList
31
44
  ? normalizeNativeFileList(opts.fileList)
@@ -54,3 +67,6 @@ export async function encodeBinaryToPng(input, opts = {}) {
54
67
  }
55
68
  }
56
69
  }
70
+ function normalizeNativeFileList(fileList) {
71
+ return JSON.stringify(fileList);
72
+ }
@@ -1,4 +1,4 @@
1
- export declare function listFilesInPng(pngBuf: Buffer, opts?: {
1
+ export declare function listFilesInPng(pngBuf: Buffer, _opts?: {
2
2
  includeSizes?: boolean;
3
3
  }): Promise<string[] | {
4
4
  name: string;
@@ -30,7 +30,7 @@ function tryExtractFileListFromChunks(chunks) {
30
30
  }
31
31
  return null;
32
32
  }
33
- export async function listFilesInPng(pngBuf, opts = {}) {
33
+ export async function listFilesInPng(pngBuf, _opts = {}) {
34
34
  try {
35
35
  const chunks = native.extractPngChunks(pngBuf);
36
36
  const result = tryExtractFileListFromChunks(chunks);
@@ -210,7 +210,7 @@ export async function hasPassphraseInPng(pngBuf) {
210
210
  idx += nameLen;
211
211
  if (valid.length < idx + 4 + 1)
212
212
  return;
213
- const payloadLen = valid.readUInt32BE(idx);
213
+ valid.readUInt32BE(idx);
214
214
  idx += 4;
215
215
  if (valid.length < idx + 1)
216
216
  return;
@@ -109,19 +109,6 @@ const GOERTZEL_COEFFS = CARRIERS.map(freq => {
109
109
  return { k, coeff: 2 * Math.cos(w) };
110
110
  });
111
111
  // ─── Signal Processing Primitives ───────────────────────────────────────────
112
- /**
113
- * Generate a raised-cosine windowed tone burst.
114
- */
115
- function generateTone(freq, numSamples, amplitude) {
116
- const out = new Float64Array(numSamples);
117
- const w = (2 * Math.PI * freq) / SAMPLE_RATE;
118
- for (let n = 0; n < numSamples; n++) {
119
- // Raised cosine window (Hann)
120
- const window = 0.5 * (1 - Math.cos((2 * Math.PI * n) / (numSamples - 1)));
121
- out[n] = amplitude * window * Math.sin(w * n);
122
- }
123
- return out;
124
- }
125
112
  /**
126
113
  * Goertzel algorithm: compute energy at a specific frequency.
127
114
  * Returns normalized power (0–1 range for unit-amplitude input).
@@ -17,12 +17,8 @@ import { native } from './native.js';
17
17
  // ─── Configuration ──────────────────────────────────────────────────────────
18
18
  /** Finder pattern (7×7 blocks, like QR codes). */
19
19
  const FINDER_SIZE = 7;
20
- /** Alignment pattern (5×5 blocks). */
21
- const ALIGNMENT_SIZE = 5;
22
20
  /** Quiet zone around finder patterns (blocks). */
23
21
  const QUIET_ZONE = 1;
24
- /** Header area width in blocks (next to top-left finder). */
25
- const HEADER_BLOCKS = 20;
26
22
  /** Magic bytes for robust image format. */
27
23
  const ROBUST_IMG_MAGIC = Buffer.from('RBI1');
28
24
  // ─── Finder Pattern ─────────────────────────────────────────────────────────
@@ -43,22 +39,6 @@ function finderPattern() {
43
39
  }
44
40
  return p;
45
41
  }
46
- /**
47
- * Generate a 5×5 alignment pattern.
48
- */
49
- function alignmentPattern() {
50
- const p = [];
51
- for (let y = 0; y < ALIGNMENT_SIZE; y++) {
52
- const row = [];
53
- for (let x = 0; x < ALIGNMENT_SIZE; x++) {
54
- const border = y === 0 || y === 4 || x === 0 || x === 4;
55
- const center = y === 2 && x === 2;
56
- row.push(border || center);
57
- }
58
- p.push(row);
59
- }
60
- return p;
61
- }
62
42
  /**
63
43
  * Compute grid layout: place finder patterns, quiet zones, and determine
64
44
  * which block positions are available for data.
@@ -294,12 +274,6 @@ const ECC_LEVEL_MAP = {
294
274
  quartile: 2,
295
275
  high: 3,
296
276
  };
297
- const ECC_LEVEL_REVERSE = {
298
- 0: 'low',
299
- 1: 'medium',
300
- 2: 'quartile',
301
- 3: 'high',
302
- };
303
277
  /**
304
278
  * Encode binary data into a lossy-resilient PNG image.
305
279
  *