clipwhit / lib /zip.js
CyberMax-tools's picture
Deploy clipwhit
be07307 verified
Raw History Blame Contribute Delete
2.45 kB
// Minimal ZIP writer (stored, no compression: MP4s don't compress). Returns a Blob. © 2026 CyberMax. All rights reserved.
const TABLE = (() => {
const t = new Uint32Array(256);
for (let n = 0; n < 256; n++) { let c = n; for (let k = 0; k < 8; k++) c = c & 1 ? 0xEDB88320 ^ (c >>> 1) : c >>> 1; t[n] = c >>> 0; }
return t;
})();
export function crc32(bytes) {
let c = 0xFFFFFFFF;
for (let i = 0; i < bytes.length; i++) c = TABLE[(c ^ bytes[i]) & 0xFF] ^ (c >>> 8);
return (c ^ 0xFFFFFFFF) >>> 0;
}
/** files: [{name, data: Uint8Array | string}] -> Uint8Array parts of a .zip (Blob-ready). */
export function zipParts(files, date = new Date()) {
const enc = new TextEncoder();
const dosTime = (date.getHours() << 11) | (date.getMinutes() << 5) | (date.getSeconds() >> 1);
const dosDate = ((date.getFullYear() - 1980) << 9) | ((date.getMonth() + 1) << 5) | date.getDate();
const parts = [], central = [];
let offset = 0;
for (const f of files) {
const name = enc.encode(f.name);
const data = typeof f.data === 'string' ? enc.encode(f.data) : f.data;
const crc = crc32(data);
const h = new DataView(new ArrayBuffer(30));
h.setUint32(0, 0x04034b50, true); h.setUint16(4, 20, true); h.setUint16(6, 0x0800, true); h.setUint16(8, 0, true);
h.setUint16(10, dosTime, true); h.setUint16(12, dosDate, true); h.setUint32(14, crc, true);
h.setUint32(18, data.length, true); h.setUint32(22, data.length, true); h.setUint16(26, name.length, true); h.setUint16(28, 0, true);
parts.push(new Uint8Array(h.buffer), name, data);
const c = new DataView(new ArrayBuffer(46));
c.setUint32(0, 0x02014b50, true); c.setUint16(4, 20, true); c.setUint16(6, 20, true); c.setUint16(8, 0x0800, true); c.setUint16(10, 0, true);
c.setUint16(12, dosTime, true); c.setUint16(14, dosDate, true); c.setUint32(16, crc, true); c.setUint32(20, data.length, true);
c.setUint32(24, data.length, true); c.setUint16(28, name.length, true); c.setUint32(42, offset, true);
central.push(new Uint8Array(c.buffer), name);
offset += 30 + name.length + data.length;
}
const size = central.reduce((a, b) => a + b.length, 0);
const end = new DataView(new ArrayBuffer(22));
end.setUint32(0, 0x06054b50, true); end.setUint16(8, files.length, true); end.setUint16(10, files.length, true);
end.setUint32(12, size, true); end.setUint32(16, offset, true);
return [...parts, ...central, new Uint8Array(end.buffer)];
}