import fs from 'fs'; import path from 'path'; import packer, { packAsync, type TexturePackerOptions } from "free-tex-packer-core" const args = process.argv.slice(2); if (args.length < 2) { console.log('Usage: sillypacker '); process.exit(1); } const inputFolder = args[0]; const outputFolder = args[1]; console.log(`Input Folder: ${inputFolder}`); console.log(`Output Folder: ${outputFolder}`); type FileImage = { path: string, contents: Buffer, } const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff']; const isImageFile = (fileName: string) => imageExtensions.some(ext => fileName.toLowerCase().endsWith(ext)); const getImageFiles = (dirPath: string): FileImage[] => { const files:FileImage[] = []; const items = fs.readdirSync(dirPath); items.forEach((item) => { const fullPath = path.join(dirPath, item); const stats = fs.statSync(fullPath); const basename = path.basename(fullPath); if (stats.isDirectory()) files.push(...getImageFiles(fullPath)); else if (stats.isFile() && isImageFile(item)) files.push({path: basename, contents: fs.readFileSync(fullPath)}); }); return files; }; const images = getImageFiles(inputFolder); const sorter = (a, b) => { let prefixA = a.match(/[a-zA-Z]+/)[0]; let numA = parseInt(a.match(/\d+/)[0]); let prefixB = b.match(/[a-zA-Z]+/)[0]; let numB = parseInt(b.match(/\d+/)[0]); if (prefixA === prefixB) { return numA - numB; } else { return prefixA.localeCompare(prefixB); } } let options:TexturePackerOptions = { textureName: path.basename(inputFolder), removeFileExtension: true, prependFolderName: false, width: 1024, height: 1024, powerOfTwo: true, padding: 2, extrude: 0, allowRotation: true, allowTrim: true, detectIdentical: true, trimMode: "trim", packer: "OptimalPacker", exporter: "JsonArray" }; packer(images, options, (files, error) => { if (error) { console.error('Packaging failed', error); } else { const meta = files.filter(i => i.name.endsWith(".json")) .map(i => JSON.parse(i.buffer.toString("utf-8"))) .map(i => { const images = i.frames.map(frame => { const name = frame.filename; const texcord_x = frame.frame.x const texcord_y = frame.frame.y const texcord_w = frame.frame.w const texcord_h = frame.frame.h const offset_x = frame.spriteSourceSize.x const offset_y = frame.spriteSourceSize.y const width = frame.sourceSize.w const height = frame.sourceSize.h const rotated = (frame.rotated) ? 1 : 0; return {name, texcord_x, texcord_y, texcord_w, texcord_h, offset_x, offset_y, width, height, rotated} }).toSorted(); const name = i.meta.image; return {name, images} }) // fs.writeFileSync( // path.join("dist", options.textureName + ".json"), // JSON.stringify(meta, null, 2), // "utf-8"); const out = [`sillyatl ${meta.length}`, meta.map(tex => [`${path.basename(tex.name, path.extname(tex.name))} ${tex.images.length}`, ...tex.images.map(img => { return `${img.name} ${img.texcord_x} ${img.texcord_y} ${img.texcord_w} ${img.texcord_h} ${img.offset_x} ${img.offset_y} ${img.width} ${img.height} ${img.rotated}` }).toSorted((a, b) => sorter(a,b))].join("\n"))].flat().join("\n"); fs.writeFileSync(path.join(outputFolder, options.textureName + ".txt"), out, "utf-8"); const texture = files.filter(i => i.name.endsWith(".png")) .map(i => fs.writeFileSync(path.join(outputFolder, i.name), i.buffer)); } });