import type { PathLike } from "fs"; import { access } from "fs/promises"; import { dirname, resolve } from "path"; import sharp, { type OverlayOptions } from "sharp"; import { parseArgs } from "util"; import { formatImage, makeKZ } from "./common"; import { readFile } from "fs/promises"; import { writeFile } from "fs/promises"; import { rm } from "fs/promises"; import { mkdir } from "fs/promises"; import { stat } from "fs/promises"; import { readdir } from "fs/promises"; const { values: args } = parseArgs({ args: Bun.argv, options: { images: { type: "string", short: "i", default: "images/images.json" }, imagedir: { type: "string", short: "d", default: "images" }, outdir: { type: "string", short: "o", default: "data" }, basefile: { type: "string", short: "b", default: "base.png" }, throw: { type: "boolean", short: "t", default: false } }, strict: true, allowPositionals: true }); const dirExists = async(path: PathLike) => access(path).then(() => true, () => false); const imagesPath = resolve(args.images ?? "images.json"); const imageDir = resolve(args.imagedir ?? dirname(imagesPath)); const baseFile = resolve(args.basefile ?? "base.png"); const outDir = resolve(args.outdir ?? "data"); const throwOnMissing = !!args.throw; if (!await dirExists(imageDir)) { console.error("Image directory %s does not exist.", imageDir); process.exit(1); } const ap = (p: string) => resolve(p, "assets/minecraft/textures/entity/bed"); // const ap = (p: string) => resolve(p, "../img"); await rm(`${outDir}/assets`, { recursive: true, force: true }); await mkdir(ap(outDir), { recursive: true }); export interface Image { animation?: { frametime?: number; }; frames?: { start: number; end?: number; } | Array; name: string; sizeMultiplier?: number; rotate?: number; flip?: "horizontal" | "vertical"; } export interface Detail { pos: [number, number]; size: [number, number]; cut: [number, number]; rotate?: number; flip?: "horizontal" | "vertical"; } export interface Details { top: Detail; middle: Detail; bottom: Detail; } export interface Config { defaultSize: [width: number, height: number]; details: Details; images: Array; } const config = await Bun.file(imagesPath).json() as Config; for (const image of config.images) { let img: string | Array; if (await stat(`${imageDir}/${image.name}`).then(s => s.isDirectory(), () => false)) { img = await readdir(`${imageDir}/${image.name}`).then(files => files.map(f => `${imageDir}/${image.name}/${f}`).sort()); } else { const [file] = await Array.fromAsync(new Bun.Glob(`${image.name}.*`).scan({ onlyFiles: true, cwd: imageDir })); if (!file) { console.error("Image %s does not exist.", image.name); if (throwOnMissing) { process.exit(1); } continue; } img = `${imageDir}/${file}` } console.log("Processing %s", image.name); const { isImage, result } = await formatImage(image.name, baseFile, config, img); await result.toFile(`${ap(outDir)}/${image.name}.png`); if (!isImage) { await writeFile(`${ap(outDir)}/${image.name}.png.mcmeta`, JSON.stringify({ animation: image.animation ?? {} })); } }