t2-mapper/scripts/extract-assets.ts

109 lines
3.2 KiB
TypeScript

import fs from "node:fs/promises";
import path from "node:path";
import { parseArgs } from "node:util";
import ignore from "ignore";
import unzipper from "unzipper";
import { normalizePath } from "@/src/stringUtils";
import { walkDirectory } from "@/src/fileUtils";
const inputBaseDir = process.env.BASE_DIR || "GameData/base";
const outputBaseDir = "docs/base";
// NOTE! Yes, the files below will be ignored. But this also expects `inputBaseDir`
// to largely have already been pruned of files that are indistinguishable from
// useful files - like player skins, voice binds, and anything else that will
// not be used by the map tool. So, remove `voice.vl2` before running this, for
// example. Random scripts are typically fine, since they're small (and other
// scripts may expect them to be available).
const ignoreList = ignore().add(`
fonts/
lighting/
prefs/
.DS_Store
*.dso
*.gui
*.ico
*.ml
*.nav
*.txt
`);
async function extractAssets({ clean }: { clean: boolean }) {
const vl2Files: string[] = [];
const looseFiles: string[] = [];
// Discover all files
await walkDirectory(inputBaseDir, {
onFile: ({ entry }) => {
const filePath = path.join(entry.parentPath, entry.name);
const resourcePath = normalizePath(path.relative(inputBaseDir, filePath));
if (!ignoreList.ignores(resourcePath)) {
if (/\.vl2$/i.test(entry.name)) {
vl2Files.push(filePath);
} else {
looseFiles.push(filePath);
}
}
},
onDir: ({ entry }) => {
const dirPath = path.join(entry.parentPath, entry.name);
const resourcePath =
normalizePath(path.relative(inputBaseDir, dirPath)) + "/";
const shouldRecurse = !ignoreList.ignores(resourcePath);
return shouldRecurse;
},
});
if (clean) {
console.log(`Cleaning ${outputBaseDir}`);
await fs.rm(outputBaseDir, { recursive: true, force: true });
}
await fs.mkdir(outputBaseDir, { recursive: true });
for (const filePath of looseFiles) {
const relativePath = path.relative(inputBaseDir, filePath);
const outFile = path.join(outputBaseDir, relativePath);
const outDir = path.dirname(outFile);
console.log(outFile);
await fs.mkdir(outDir, { recursive: true });
await fs.copyFile(filePath, outFile);
}
// Extract .vl2 files
for (const archivePath of vl2Files) {
const relativePath = path.relative(inputBaseDir, archivePath);
const archive = await unzipper.Open.file(archivePath);
const outputArchiveDir = path.join(outputBaseDir, "@vl2", relativePath);
for (const entry of archive.files) {
if (entry.type === "Directory") continue;
const resourcePath = normalizePath(entry.path);
if (ignoreList.ignores(resourcePath)) continue;
const outFile = path.join(outputArchiveDir, resourcePath);
const outDir = path.dirname(outFile);
console.log(outFile);
await fs.mkdir(outDir, { recursive: true });
const content = await entry.buffer();
await fs.writeFile(outFile, content);
}
}
console.log("Done.");
}
const { values } = parseArgs({
options: {
clean: {
type: "boolean",
default: false,
},
},
});
extractAssets({ clean: values.clean });