import { mkdir, writeFile } from "node:fs/promises"; import { createHash } from "node:crypto"; import { gzipSync } from "node:zlib"; import { encodePath } from "../src/lib/full-codec.js"; export async function prepareStream(packed, source) { const { metadata, buffer } = packed; const version = createHash("sha256").update("fdm-stream-v2-shell24\n").update(source).digest("hex").slice(0, 16); const base = new URL(`../public/data/stream/${version}/`, import.meta.url); await mkdir(base, { recursive: true }); const lines = source.split(/\r?\n/); const starts = [0, ...metadata.layers.map((layer) => layer.segment)].filter((v, i, a) => !i || v !== a[i - 1]); const chunks = []; const overview = []; const allCommands = {}; for (let id = 0; id < starts.length; id++) { const start = starts[id]; const end = starts[id + 1] ?? metadata.count; const part = buffer.subarray(start * 12, end * 12); const commands = {}; const shell = []; for (let index = start; index < end; index++) { const o = index * 12; commands[buffer[o + 11]] = lines[buffer[o + 11] - 1].trim(); allCommands[buffer[o + 11]] = commands[buffer[o + 11]]; if (buffer[o + 8] > 0 && ["Outer wall", "Overhang wall", "Top surface", "Bottom surface"].includes(metadata.types[buffer[o + 10]])) shell.push(index); } const step = Math.max(1, Math.ceil(shell.length / 24)); for (let i = 0; i < shell.length; i += step) { const index = shell[i]; overview.push(index, ...buffer.subarray(index * 12, index * 12 + 12)); } chunks.push({ id, start, end, startTime: start ? buffer[(start - 1) * 12 + 6] : 0, endTime: buffer[(end - 1) * 12 + 6] }); await writeFile(new URL(`${id}.bin`, base), Buffer.from(part.buffer, part.byteOffset, part.byteLength)); await writeFile(new URL(`${id}.json`, base), JSON.stringify(commands)); } await writeFile(new URL("overview.bin", base), Buffer.from(new Float32Array(overview).buffer)); await writeFile(new URL("full.bin", base), Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength)); await writeFile(new URL("commands.json", base), JSON.stringify(allCommands)); const fullVersion = createHash("sha256").update("fdm-full-v2-delta\n").update(source).digest("hex").slice(0, 16); const fullBase = new URL(`../public/data/full/${fullVersion}/`, import.meta.url); await mkdir(fullBase, { recursive: true }); const commandText = Array.from({ length: metadata.count }, (_, index) => allCommands[buffer[index * 12 + 11]]).join("\n") + "\n"; const full = { decodedSha256: createHash("sha256").update(Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength)).digest("hex") }; for (const [key, content] of [["path", encodePath(buffer)], ["commands", Buffer.from(commandText)]]) { const compressed = gzipSync(content, { level: 9 }); const file = `${key}.fdmz`; full[key] = { file, bytes: compressed.length, sha256: createHash("sha256").update(compressed).digest("hex") }; await writeFile(new URL(file, fullBase), compressed); } await writeFile(new URL("../public/data/stream.json", import.meta.url), JSON.stringify({ ...metadata, version, chunks, fullVersion, full })); console.log(`完整数据无损压缩:${((full.path.bytes + full.commands.bytes) / 1048576).toFixed(2)} MiB`); console.log(`按层数据:${chunks.length} 块,概览 ${overview.length / 13} 段`); }