mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-02-12 19:31:47 +00:00
Initial commit
This commit is contained in:
commit
2211ed7650
10117 changed files with 735995 additions and 0 deletions
47
scripts/extract.ts
Normal file
47
scripts/extract.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import fs from "node:fs/promises";
|
||||
import unzipper from "unzipper";
|
||||
import { normalize } from "@/src/stringUtils";
|
||||
import manifest from "@/public/manifest.json";
|
||||
import path from "node:path";
|
||||
|
||||
const inputBaseDir = "rawGameData/base";
|
||||
const outputBaseDir = "public/base";
|
||||
|
||||
const archives = new Map<string, unzipper.CentralDirectory>();
|
||||
|
||||
async function buildExtractedGameDataFolder() {
|
||||
await fs.mkdir(outputBaseDir, { recursive: true });
|
||||
const filePaths = Object.keys(manifest).sort();
|
||||
for (const filePath of filePaths) {
|
||||
const sources = manifest[filePath];
|
||||
for (const source of sources) {
|
||||
if (source) {
|
||||
let archive = archives.get(source);
|
||||
if (!archive) {
|
||||
const archivePath = `${inputBaseDir}/${source}`;
|
||||
archive = await unzipper.Open.file(archivePath);
|
||||
archives.set(source, archive);
|
||||
}
|
||||
const entry = archive.files.find(
|
||||
(entry) => normalize(entry.path) === filePath
|
||||
);
|
||||
const inFile = `${inputBaseDir}/${source}:${filePath}`;
|
||||
if (!entry) {
|
||||
throw new Error(`File not found in archive: ${inFile}`);
|
||||
}
|
||||
const outFile = `${outputBaseDir}/@vl2/${source}/${filePath}`;
|
||||
const outDir = path.dirname(outFile);
|
||||
console.log(`${inFile} -> ${outFile}`);
|
||||
await fs.mkdir(outDir, { recursive: true });
|
||||
await fs.writeFile(outFile, entry.stream());
|
||||
} else {
|
||||
const inFile = `${inputBaseDir}/${filePath}`;
|
||||
const outFile = `${outputBaseDir}/${filePath}`;
|
||||
console.log(`${inFile} -> ${outFile}`);
|
||||
await fs.cp(inFile, outFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildExtractedGameDataFolder();
|
||||
17
scripts/interior.ts
Normal file
17
scripts/interior.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import fs from "node:fs";
|
||||
import { inspect } from "node:util";
|
||||
import { parseInteriorBuffer } from "@/src/interior";
|
||||
|
||||
const interiorFile = process.argv[2];
|
||||
const interiorBuffer = fs.readFileSync(interiorFile);
|
||||
const interiorArrayBuffer = interiorBuffer.buffer.slice(
|
||||
interiorBuffer.byteOffset,
|
||||
interiorBuffer.byteOffset + interiorBuffer.byteLength
|
||||
);
|
||||
|
||||
console.log(
|
||||
inspect(parseInteriorBuffer(interiorArrayBuffer), {
|
||||
colors: true,
|
||||
depth: Infinity,
|
||||
})
|
||||
);
|
||||
86
scripts/manifest.ts
Normal file
86
scripts/manifest.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { parseArgs } from "node:util";
|
||||
import unzipper from "unzipper";
|
||||
import { normalize } from "@/src/stringUtils";
|
||||
|
||||
const archiveFilePattern = /\.vl2$/i;
|
||||
|
||||
const baseDir = "rawGameData/base";
|
||||
|
||||
function isArchive(name: string) {
|
||||
return archiveFilePattern.test(name);
|
||||
}
|
||||
|
||||
async function buildManifest() {
|
||||
const fileSources = new Map<string, string[]>();
|
||||
|
||||
const looseFiles: string[] = [];
|
||||
const archiveFiles: string[] = [];
|
||||
for await (const entry of fs.glob(`${baseDir}/**/*`, {
|
||||
withFileTypes: true,
|
||||
})) {
|
||||
if (entry.isFile()) {
|
||||
const fullPath = normalize(`${entry.parentPath}/${entry.name}`);
|
||||
if (isArchive(entry.name)) {
|
||||
archiveFiles.push(fullPath);
|
||||
} else {
|
||||
looseFiles.push(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const filePath of looseFiles) {
|
||||
const relativePath = normalize(path.relative(baseDir, filePath));
|
||||
fileSources.set(relativePath, [""]);
|
||||
}
|
||||
|
||||
archiveFiles.sort();
|
||||
for (const archivePath of archiveFiles) {
|
||||
const relativePath = normalize(path.relative(baseDir, archivePath));
|
||||
const archive = await unzipper.Open.file(archivePath);
|
||||
for (const archiveEntry of archive.files) {
|
||||
if (archiveEntry.type === "File") {
|
||||
const filePath = normalize(archiveEntry.path);
|
||||
const sources = fileSources.get(filePath) ?? [];
|
||||
sources.push(relativePath);
|
||||
fileSources.set(filePath, sources);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const manifest: Record<string, string[]> = {};
|
||||
|
||||
const orderedFiles = Array.from(fileSources.keys()).sort();
|
||||
for (const filePath of orderedFiles) {
|
||||
const sources = fileSources.get(filePath);
|
||||
manifest[filePath] = sources;
|
||||
console.log(
|
||||
`${filePath}${sources[0] ? ` 📦 ${sources[0]}` : ""}${
|
||||
sources.length > 1
|
||||
? sources
|
||||
.slice(1)
|
||||
.map((source) => ` ❗️ ${source}`)
|
||||
.join("")
|
||||
: ""
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
return manifest;
|
||||
}
|
||||
|
||||
const { values } = parseArgs({
|
||||
options: {
|
||||
output: {
|
||||
type: "string",
|
||||
short: "o",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const manifest = await buildManifest();
|
||||
|
||||
if (values.output) {
|
||||
await fs.writeFile(values.output, JSON.stringify(manifest), "utf8");
|
||||
}
|
||||
61
scripts/mission.ts
Normal file
61
scripts/mission.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import fs from "node:fs";
|
||||
import { inspect, parseArgs } from "node:util";
|
||||
import { parseMissionScript } from "@/src/mission";
|
||||
import { getFilePath } from "@/src/manifest";
|
||||
|
||||
async function run() {
|
||||
const { values, positionals } = parseArgs({
|
||||
allowPositionals: true,
|
||||
options: {
|
||||
name: {
|
||||
type: "string",
|
||||
short: "n",
|
||||
},
|
||||
list: {
|
||||
type: "boolean",
|
||||
short: "l",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (values.list) {
|
||||
if (values.name || positionals[0]) {
|
||||
console.error("Cannot specify --list (-l) with other options.");
|
||||
return 1;
|
||||
}
|
||||
const manifest = (await import("../public/manifest.json")).default;
|
||||
const fileNames = Object.keys(manifest);
|
||||
console.log(
|
||||
fileNames
|
||||
.map((f) => f.match(/^missions\/(.+)\.mis$/))
|
||||
.filter(Boolean)
|
||||
.map((match) => match[1])
|
||||
.join("\n")
|
||||
);
|
||||
return;
|
||||
} else if (
|
||||
(values.name && positionals[0]) ||
|
||||
(!values.name && !positionals[0])
|
||||
) {
|
||||
console.error(
|
||||
"Must specify exactly one of --name (-n) or a positional filename."
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
let missionFile = positionals[0];
|
||||
if (values.name) {
|
||||
const resourcePath = `missions/${values.name}.mis`;
|
||||
missionFile = getFilePath(resourcePath);
|
||||
}
|
||||
const missionScript = fs.readFileSync(missionFile, "utf8");
|
||||
console.log(
|
||||
inspect(parseMissionScript(missionScript), {
|
||||
colors: false,
|
||||
depth: Infinity,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const code = await run();
|
||||
process.exit(code ?? 0);
|
||||
66
scripts/terrain.ts
Normal file
66
scripts/terrain.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import fs from "node:fs";
|
||||
import { inspect, parseArgs } from "node:util";
|
||||
import { parseTerrainBuffer } from "@/src/terrain";
|
||||
import { getFilePath } from "@/src/manifest";
|
||||
|
||||
async function run() {
|
||||
const { values, positionals } = parseArgs({
|
||||
allowPositionals: true,
|
||||
options: {
|
||||
name: {
|
||||
type: "string",
|
||||
short: "n",
|
||||
},
|
||||
list: {
|
||||
type: "boolean",
|
||||
short: "l",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (values.list) {
|
||||
if (values.name || positionals[0]) {
|
||||
console.error("Cannot specify --list (-l) with other options.");
|
||||
return 1;
|
||||
}
|
||||
const manifest = (await import("../public/manifest.json")).default;
|
||||
const fileNames = Object.keys(manifest);
|
||||
console.log(
|
||||
fileNames
|
||||
.map((f) => f.match(/^terrains\/(.+)\.ter$/))
|
||||
.filter(Boolean)
|
||||
.map((match) => match[1])
|
||||
.join("\n")
|
||||
);
|
||||
return;
|
||||
} else if (
|
||||
(values.name && positionals[0]) ||
|
||||
(!values.name && !positionals[0])
|
||||
) {
|
||||
console.error(
|
||||
"Must specify exactly one of --name (-n) or a positional filename."
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
let terrainFile = positionals[0];
|
||||
if (values.name) {
|
||||
const resourcePath = `terrains/${values.name}.ter`;
|
||||
terrainFile = getFilePath(resourcePath);
|
||||
}
|
||||
const terrainBuffer = fs.readFileSync(terrainFile);
|
||||
const terrainArrayBuffer = terrainBuffer.buffer.slice(
|
||||
terrainBuffer.byteOffset,
|
||||
terrainBuffer.byteOffset + terrainBuffer.byteLength
|
||||
);
|
||||
|
||||
console.log(
|
||||
inspect(parseTerrainBuffer(terrainArrayBuffer), {
|
||||
colors: true,
|
||||
depth: Infinity,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const code = await run();
|
||||
process.exit(code ?? 0);
|
||||
Loading…
Add table
Add a link
Reference in a new issue