shithub: scrax

ref: 81d1e4e01d7001e6a0107792e6167136787295d6
dir: /zip.js/

View raw version
let encoder = new TextEncoder()
let decoder = new TextDecoder()

export async function zip(files)
{
	let size = 0
	let offset = 0
	let main = []
	let central = []
	for (let file of files) {
		let info = await getInfo(file, offset)
		main.push(info.main)
		central.push(info.central)
		offset += info.main.size
		size += info.central.size
	}
	return new Blob([...main, ...central, new Uint8Array([0x50, 0x4B, 5, 6]), new Uint16Array([0, 0, files.length, files.length]), new Uint32Array([size, offset]), new Uint16Array([0])], {type: "application/zip"})
}

async function getInfo(file, offset)
{
	let bytes = new Uint8Array(await file.arrayBuffer())
	let name = encoder.encode(file.name)
	let crc32 = CRC32(bytes)
	let main = new Blob([new Uint8Array([0x50, 0x4B, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), new Uint32Array([crc32, bytes.length, bytes.length]), new Uint16Array([name.length, 0]), name, file])
	let central = new Blob([new Uint8Array([0x50, 0x4B, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), new Uint32Array([crc32, bytes.length, bytes.length]), new Uint16Array([name.length, 0, 0, 0, 0]), new Uint32Array([0, offset]), name])
	return {main, central}
}

let table = []
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
	table.push(c)
}

function CRC32(bytes)
{
	let crc = 0 ^ -1
	for (let i = 0 ; i < bytes.length ; i++) crc = (crc >>> 8) ^ table[(crc ^ bytes[i]) & 0xFF]
	return crc ^ -1
}

export async function unzip(bytes)
{
	let n = bytes.length - 22
	while (true) {
		if (n < 0) return
		if (bytes[n] === 0x50 && bytes[n + 1] === 0x4B && bytes[n + 2] === 5 && bytes[n + 3] === 6) {
			break
		}
		n--
	}
	
	let array = []
	let uint
	let read = length =>
	{
		array = []
		uint = 0
		for (let i = 0 ; i < length ; i++) {
			if (n > bytes.length - 1) return true
			uint += bytes[n] * 0x100 ** i
			array.push(bytes[n])
			n++
		}
	}
	
	if (read(4)) return
	if (read(2)) return
	let index = uint
	if (read(2)) return
	if (uint !== index) return
	if (read(2)) return
	let count = uint
	if (read(2)) return
	if (uint !== count) return
	if (read(4)) return
	if (read(4)) return
	let offset = uint
	if (read(2)) return
	if (uint !== bytes.length - n) return
	
	let files = new Map()
	let m = offset
	
	for (let i = 0 ; i < count ; i++) {
		
		n = m
		if (read(4)) return
		if (uint !== 0x02014B50) return
		if (read(2)) return
		if (read(2)) return
		if (read(2)) return
		let flags = uint
		if (read(2)) return
		let compression = uint
		if (compression !== 0 && compression !== 8) return
		if (read(2)) return
		if (read(2)) return
		if (read(4)) return
		if (read(4)) return
		let length = uint
		if (read(4)) return
		let totalLength = uint
		if (read(2)) return
		let nameLength = uint
		if (read(2)) return
		let extraLength = uint
		if (read(2)) return
		let commentLength = uint
		if (read(2)) return
		if (uint !== index) return
		if (read(2)) return
		if (read(4)) return
		if (read(4)) return
		let offset = uint
		if (read(nameLength)) return
		let name = decoder.decode(new Uint8Array(array))
		if (read(extraLength)) return
		if (read(commentLength)) return
		m = n
		
		n = offset
		if (read(4)) return
		if (uint !== 0x04034B50) return
		if (read(2)) return
		if (read(2)) return
		if (uint !== flags) return
		if (read(2)) return
		if (uint !== compression) return
		if (read(2)) return
		if (read(2)) return
		if (read(4)) return
		if (read(4)) return
		if (uint !== length)
		if (read(4)) return
		if (read(4)) return
		if (uint !== totalLength) return
		if (read(2)) return
		if (uint !== nameLength) return
		if (read(2)) return
		let localExtraLength = uint
		if (read(nameLength)) return
		if (decoder.decode(new Uint8Array(array)) !== name) return
		if (read(localExtraLength)) return
		if (read(length)) return
		
		let bytes = new Uint8Array(array)
		if (compression) {
			try { bytes = new Uint8Array(await new Response(new Blob([bytes]).stream().pipeThrough(new DecompressionStream("deflate-raw"))).arrayBuffer()) }
			catch { return }
		}
		if (bytes.length !== totalLength) return
		files.set(name, bytes)
	}
	
	return files
}