51 lines
2.1 KiB
JavaScript
51 lines
2.1 KiB
JavaScript
// zip.js —— 零依赖 ZIP 解压(stored/deflate),供技能安装与热更新复用。
|
||
"use strict";
|
||
const fs = require("fs");
|
||
const path = require("path");
|
||
const zlib = require("zlib");
|
||
|
||
// 解析中央目录,把 zip 内容写入 destDir;返回写出的文件数。
|
||
function extractZip(buf, destDir) {
|
||
// 找 EOCD(0x06054b50),从尾部回扫
|
||
let eocd = -1;
|
||
for (let i = buf.length - 22; i >= 0 && i >= buf.length - 22 - 65536; i--) {
|
||
if (buf.readUInt32LE(i) === 0x06054b50) { eocd = i; break; }
|
||
}
|
||
if (eocd < 0) throw new Error("不是有效的 zip(找不到 EOCD)");
|
||
const count = buf.readUInt16LE(eocd + 10);
|
||
let ptr = buf.readUInt32LE(eocd + 16); // 中央目录起始偏移
|
||
let written = 0;
|
||
for (let n = 0; n < count; n++) {
|
||
if (buf.readUInt32LE(ptr) !== 0x02014b50) break;
|
||
const method = buf.readUInt16LE(ptr + 10);
|
||
const compSize = buf.readUInt32LE(ptr + 20);
|
||
const nameLen = buf.readUInt16LE(ptr + 28);
|
||
const extraLen = buf.readUInt16LE(ptr + 30);
|
||
const commentLen = buf.readUInt16LE(ptr + 32);
|
||
const localOff = buf.readUInt32LE(ptr + 42);
|
||
const name = buf.toString("utf8", ptr + 46, ptr + 46 + nameLen);
|
||
ptr += 46 + nameLen + extraLen + commentLen;
|
||
// 读本地头,定位数据
|
||
if (buf.readUInt32LE(localOff) !== 0x04034b50) continue;
|
||
const lNameLen = buf.readUInt16LE(localOff + 26);
|
||
const lExtraLen = buf.readUInt16LE(localOff + 28);
|
||
const dataStart = localOff + 30 + lNameLen + lExtraLen;
|
||
const comp = buf.subarray(dataStart, dataStart + compSize);
|
||
// 防目录穿越
|
||
const safe = name.replace(/\\/g, "/").replace(/^\/+/, "");
|
||
if (safe.includes("../")) continue;
|
||
const outPath = path.join(destDir, safe);
|
||
if (safe.endsWith("/")) { fs.mkdirSync(outPath, { recursive: true }); continue; }
|
||
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
||
let data;
|
||
if (method === 0) data = comp;
|
||
else if (method === 8) data = zlib.inflateRawSync(comp);
|
||
else throw new Error(`不支持的压缩方式 ${method}`);
|
||
fs.writeFileSync(outPath, data);
|
||
written++;
|
||
}
|
||
return written;
|
||
}
|
||
|
||
module.exports = { extractZip };
|