To use this plugin, go Tools > Resource Pack Utilities, then select the utility you would like to use.
Utilities
+
+
Batch Exporter
+
Batch Exporter is a tool that will export every bbmodel file within a folder to an output folder using the selected format.
+
CIT Optimiser
CIT Optimiser is a tool that will go through all properties files in an OptiFine CIT folder and optimise them to be as small as possible, removing any unnecessary data.
@@ -2442,7 +2497,7 @@
} else {
await cacheDirectory()
assetsIndex = await getVersionAssetsIndex(this.version)
- const files = await fs.promises.readdir(langPath)
+ const files = await listFiles(langPath)
root = await getRoot(this.version)
const extension = path.extname(Object.keys(assetsIndex.objects).find(e => e.startsWith("assets/minecraft/lang/".slice(root.length + 1))))
langs = files.filter(e => assetsIndex.objects[`assets/minecraft/lang/${e}`.slice(root.length + 1)] || (e.toLowerCase().startsWith("en_us.") && e.endsWith(extension)))
@@ -2533,6 +2588,185 @@
`
}
+ },
+ batchExporter: {
+ name: "Batch Exporter",
+ icon: "move_group",
+ tagline: "Export every bbmodel file in a folder at the same time.",
+ description: "Batch Exporter is a tool that will export every bbmodel file within a folder to an output folder using the selected format.",
+ component: {
+ data: {
+ inputFolder: "",
+ outputFolder: "",
+ outputLog,
+ done: 0,
+ total: null,
+ cancelled: false,
+ textures: true,
+ subfolders: false,
+ textureFolders: true,
+ format: "java_block",
+ formats: Object.fromEntries(Object.entries(batchExporterFormats).map(e => [e[0], e[1].name])),
+ specialFormats: batchExporterSpecialFormats
+ },
+ methods: {
+ async execute() {
+ outputLog.length = 0
+ this.status.finished = false
+ this.status.processing = true
+ this.done = 0
+ this.total = null
+ this.cancelled = false
+
+ if (!await exists(this.inputFolder)) {
+ this.status.finished = true
+ this.status.processing = false
+ this.total = 0
+ output.error(`The folder \`${formatPath(this.inputFolder)}\` was not found`)
+ return
+ }
+
+ if (!await exists(this.outputFolder)) {
+ this.status.finished = true
+ this.status.processing = false
+ this.total = 0
+ output.error(`The folder \`${formatPath(this.outputFolder)}\` was not found`)
+ return
+ }
+
+ const fileList = await listFiles(this.inputFolder, { withFileTypes: true })
+ const files = []
+ for (const file of fileList) {
+ if (file.endsWith(".bbmodel")) {
+ files.push(file)
+ }
+ }
+
+ this.total = files.length
+
+ if (!files.length) {
+ this.status.finished = true
+ this.status.processing = false
+ output.error("No `.bbmodel` files present in the selected folder")
+ return
+ }
+
+ let exportOptions = {}
+ if (Object.keys(Codecs[this.format].export_options).length) {
+ output.log("Getting export options…")
+ newProject("")
+ await Codecs[this.format].promptExportOptions()
+ exportOptions = Codecs[this.format].getExportOptions()
+ await Project.close()
+ output.log("Export options loaded")
+ }
+
+ for (const file of files) {
+ const name = file.slice(0, -8)
+ let outputPath = ""
+ let fullOutputPath = this.outputFolder
+ if (this.subfolders) {
+ outputPath = name + "/"
+ fullOutputPath = path.join(fullOutputPath, name)
+ }
+ const saveName = path.join(fullOutputPath, `${name}.${batchExporterFormats[this.format].type}`)
+ if (await exists(saveName)) {
+ output.warn(`Skipping \`${file}\` as \`${outputPath}${name}.${batchExporterFormats[this.format].type}\` already exists`)
+ this.done++
+ continue
+ }
+ let data
+ try {
+ data = JSON.parse(await fs.promises.readFile(path.join(this.inputFolder, file)))
+ } catch {
+ output.error(`Skipping \`${file}\` as it could not be read`)
+ this.done++
+ continue
+ }
+ if (data.meta.model_format !== this.format && !batchExporterSpecialFormats.includes(this.format)) {
+ output.warn(`Skipping \`${file}\` as it is in ${data.meta.model_format in Formats ? `the \`${Formats[data.meta.model_format].name}\`` : "an unknown"} format`)
+ this.done++
+ continue
+ }
+ newProject(Formats[data.meta.model_format])
+ Codecs.project.parse(data)
+ let compiled, mtl
+ if (this.format === "obj") {
+ const obj = Codecs.obj.compile(Object.assign({
+ all_files: true,
+ mtl_name: this.textures ? `${name}.mtl` : undefined
+ }, exportOptions))
+ compiled = obj.obj
+ mtl = obj.mtl
+ } else {
+ compiled = await Codecs[this.format].compile(exportOptions)
+ }
+ if (fullOutputPath !== this.outputFolder) {
+ await fs.promises.mkdir(fullOutputPath, { recursive: true })
+ }
+ await fs.promises.writeFile(saveName, Buffer.from(compiled), "utf-8")
+ output.log(`Exported \`${file}\` to \`${outputPath}${name}.${batchExporterFormats[this.format].type}\``)
+ if (this.format === "obj" && this.textures) {
+ if (await exists(saveName.slice(0, -3) + "mtl")) {
+ output.warn(`Skipping \`${file}\`'s material as \`${outputPath}${name}.mtl\` already exists`)
+ } else {
+ await fs.promises.writeFile(saveName.slice(0, -3) + "mtl", mtl, "utf-8")
+ output.log(`Exported \`${file}\`'s material to \`${outputPath}${name}.mtl\``)
+ }
+ }
+ if (this.textures) {
+ for (const texture of data.textures) {
+ const name = texture.name.endsWith(".png") ? texture.name : texture.name + ".png"
+ let saveName
+ if (this.textureFolders && !batchExporterSpecialFormats.includes(this.format)) {
+ await fs.promises.mkdir(path.join(this.outputFolder, outputPath, "textures", texture.folder), { recursive: true })
+ saveName = formatPath(path.join(outputPath, "textures", texture.folder, name))
+ } else {
+ saveName = `${outputPath}${name}`
+ }
+ const savePath = path.join(this.outputFolder, saveName)
+ if (await exists(savePath)) {
+ output.warn(`Skipping texture \`${name}\` from \`${file}\` as \`${saveName}\` already exists`)
+ continue
+ }
+ await fs.promises.writeFile(savePath, Buffer.from(texture.source.split(",")[1], "base64"), "utf-8")
+ output.log(`Exported \`${name}\` from \`${file}\` to \`${saveName}\``)
+ }
+ }
+ await Project.close()
+ this.done++
+ }
+
+ output.info("Finished")
+ this.status.finished = true
+ this.status.processing = false
+ }
+ },
+ styles: `
+ .component-versionSelector {
+ align-self: flex-start;
+ }
+ `,
+ template: `
+
+
Input Folder:
+ the folder containing bbmodels
+
Output Folder:
+ the folder to export the bbmodels to
+ Output format
+ Export each model to its own subfolder
+ Export textures
+ Export textures into their defined folders
+
+