// parseZip — разбор ZIP: центральный каталог → записи {name, data, dosMs, isDir}. // Поддерживаются методы 0 (store) и 8 (deflate). Без внешних библиотек. import { decodeZipName } from './decode_zip_name.js'; import { inflateRaw } from './inflate_raw.js'; import { dosToMs } from './dos_to_ms.js'; export async function parseZip(buf) { const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); let eocd = -1; for (let i = buf.length - 22; i >= 0; i--) { if (dv.getUint32(i, true) === 0x06054b50) { eocd = i; break; } } if (eocd < 0) throw new Error('Не ZIP'); const cdSize = dv.getUint32(eocd + 12, true); const cdOffset = dv.getUint32(eocd + 16, true); const entries = []; let pos = cdOffset; const cdEnd = cdOffset + cdSize; while (pos < cdEnd) { if (dv.getUint32(pos, true) !== 0x02014b50) break; const flags = dv.getUint16(pos + 8, true); const method = dv.getUint16(pos + 10, true); const modTime = dv.getUint16(pos + 12, true); const modDate = dv.getUint16(pos + 14, true); const compSize = dv.getUint32(pos + 20, true); const nameLen = dv.getUint16(pos + 28, true); const extraLen = dv.getUint16(pos + 30, true); const commentLen = dv.getUint16(pos + 32, true); const localOffset = dv.getUint32(pos + 42, true); const nameBytes = buf.slice(pos + 46, pos + 46 + nameLen); const name = decodeZipName(nameBytes, (flags & 0x800) !== 0); const lhNameLen = dv.getUint16(localOffset + 26, true); const lhExtraLen = dv.getUint16(localOffset + 28, true); const dataStart = localOffset + 30 + lhNameLen + lhExtraLen; const comp = buf.slice(dataStart, dataStart + compSize); let data; if (method === 0) data = comp; else if (method === 8) data = await inflateRaw(comp); else throw new Error('Метод сжатия ' + method + ' не поддерживается'); entries.push({ name, data, dosMs: dosToMs(modDate, modTime), isDir: name.endsWith('/') }); pos += 46 + nameLen + extraLen + commentLen; } return entries; }