Files
SpineParticlesWeb/connector/zip-archive.mjs
T
2026-09-05 16:20:23 +08:00

79 lines
2.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { normalize } from 'node:path'
const CRC_TABLE = Array.from({ length: 256 }, (_, value) => {
let crc = value
for (let bit = 0; bit < 8; bit++) crc = (crc & 1) ? 0xedb88320 ^ (crc >>> 1) : crc >>> 1
return crc >>> 0
})
function crc32(data) {
let crc = 0xffffffff
for (const byte of data) crc = CRC_TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8)
return (crc ^ 0xffffffff) >>> 0
}
function archivePath(value) {
const path = normalize(String(value || '').replace(/\\/g, '/')).replace(/\\/g, '/').replace(/^\.\//, '')
if (!path || path.startsWith('/') || path === '..' || path.startsWith('../')) throw new Error('ZIP 文件路径无效')
return path
}
function dosDateTime(date = new Date()) {
const year = Math.max(1980, date.getFullYear())
return {
time: (date.getHours() << 11) | (date.getMinutes() << 5) | Math.floor(date.getSeconds() / 2),
date: ((year - 1980) << 9) | ((date.getMonth() + 1) << 5) | date.getDate(),
}
}
/** 创建无额外依赖的 ZIPStore 模式);PNG 和 .spine 本身已压缩,无需重复压缩。 */
export function createZipArchive(files) {
const localParts = []
const centralParts = []
let offset = 0
const timestamp = dosDateTime()
for (const file of files) {
const name = Buffer.from(archivePath(file.name), 'utf8')
const data = Buffer.isBuffer(file.data) ? file.data : Buffer.from(file.data)
const crc = crc32(data)
const local = Buffer.alloc(30)
local.writeUInt32LE(0x04034b50, 0)
local.writeUInt16LE(20, 4)
local.writeUInt16LE(0x0800, 6)
local.writeUInt16LE(0, 8)
local.writeUInt16LE(timestamp.time, 10)
local.writeUInt16LE(timestamp.date, 12)
local.writeUInt32LE(crc, 14)
local.writeUInt32LE(data.length, 18)
local.writeUInt32LE(data.length, 22)
local.writeUInt16LE(name.length, 26)
localParts.push(local, name, data)
const central = Buffer.alloc(46)
central.writeUInt32LE(0x02014b50, 0)
central.writeUInt16LE(20, 4)
central.writeUInt16LE(20, 6)
central.writeUInt16LE(0x0800, 8)
central.writeUInt16LE(0, 10)
central.writeUInt16LE(timestamp.time, 12)
central.writeUInt16LE(timestamp.date, 14)
central.writeUInt32LE(crc, 16)
central.writeUInt32LE(data.length, 20)
central.writeUInt32LE(data.length, 24)
central.writeUInt16LE(name.length, 28)
central.writeUInt32LE(offset, 42)
centralParts.push(central, name)
offset += local.length + name.length + data.length
}
const centralDirectory = Buffer.concat(centralParts)
const end = Buffer.alloc(22)
end.writeUInt32LE(0x06054b50, 0)
end.writeUInt16LE(files.length, 8)
end.writeUInt16LE(files.length, 10)
end.writeUInt32LE(centralDirectory.length, 12)
end.writeUInt32LE(offset, 16)
return Buffer.concat([...localParts, centralDirectory, end])
}