Files
Tectum/js/core/zip-writer.js
T

71 lines
2.6 KiB
JavaScript

/*!
* 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 };
})();