/*! * Tectum * Copyright © 2026 Machiel Heinen. * Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming. * Zie LICENTIE.md. */ // ZIP zonder compressie (stored): meerdere bestanden in één download. window.ZipWriter = (() => { const CRC = 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; CRC[n] = c >>> 0; } function crc32(parts) { let c = 0xffffffff; parts.forEach(b => { for (let i = 0; i < b.length; i++) c = CRC[(c ^ b[i]) & 0xff] ^ (c >>> 8); }); return (c ^ 0xffffffff) >>> 0; } // files: [{ name, data: Uint8Array | Uint8Array[] }] function build(files) { const enc = new TextEncoder(); const out = [], central = []; let offset = 0; const d = new Date(); const time = (d.getHours() << 11) | (d.getMinutes() << 5) | (d.getSeconds() >> 1); const date = ((d.getFullYear() - 1980) << 9) | ((d.getMonth() + 1) << 5) | d.getDate(); files.forEach(f => { const parts = Array.isArray(f.data) ? f.data : [f.data]; const size = parts.reduce((a, b) => a + b.length, 0); const name = enc.encode(f.name), crc = crc32(parts); const head = (sig, extra) => { const h = new DataView(new ArrayBuffer(extra ? 46 : 30)); let p = 0; const u32 = (v) => { h.setUint32(p, v, true); p += 4; }, u16 = (v) => { h.setUint16(p, v, true); p += 2; }; u32(sig); if (extra) u16(20); u16(20); u16(0x0800); u16(0); u16(time); u16(date); u32(crc); u32(size); u32(size); u16(name.length); u16(0); if (extra) { u16(0); u16(0); u16(0); u32(0); u32(offset); } return new Uint8Array(h.buffer); }; const local = head(0x04034b50, false); central.push(head(0x02014b50, true), name); out.push(local, name, ...parts); offset += local.length + name.length + size; }); const cdSize = 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, cdSize, true); end.setUint32(16, offset, true); return out.concat(central, [new Uint8Array(end.buffer)]); } function save(files, filename) { const blob = new Blob(build(files), { type: 'application/zip' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } return { build, save }; })();