Skip to content

Instantly share code, notes, and snippets.

@Bloxs
Created February 2, 2024 00:48
Show Gist options
  • Save Bloxs/247518be063ae57da22f1c0ca6ac2521 to your computer and use it in GitHub Desktop.
Save Bloxs/247518be063ae57da22f1c0ca6ac2521 to your computer and use it in GitHub Desktop.
Geometry Dash Asset Exporter

Geometry Dash Asset Exporter

This script is made for exporting the image sheets in Geometry Dash (Ie WorldSheet, GameSheet, etc)

Setup

  1. Download this script & Deno
  2. Create an assets folder
  3. Copy the respective .png and .plist for your asset into the folder
  4. Run the script
  5. Provide the name of the file without the extension
  6. You're done, just check out the out folder
import { parse } from "npm:fast-plist";
import { Image } from "https://deno.land/x/[email protected]/mod.ts";
const file = prompt(
"Enter the name of the asset file in ./assets/ without the .png or .plist extension"
);
const returnTextFileIfExists = async (file: string) => {
try {
return await Deno.readTextFile(file);
} catch {
return null;
}
};
const returnFileIfExists = async (file: string) => {
try {
return await Deno.readFile(file);
} catch {
return null;
}
};
interface GDPlist {
frames: {
[key: string]: {
// I don't know what type this is as I haven't found any instances of it, not like it's needed anyways - Bloxs
aliases: unknown[];
spriteOffset: string;
spriteSize: string;
spriteSourceSize: string;
textureRect: string;
textureRotated: boolean;
};
};
metadata: {
format: number;
pixelFormat: string;
premultiplyAlpha: boolean;
realTextureFileName: string;
size: string;
smartupdate: string;
textureFileName: string;
};
}
const xmlFile = await returnTextFileIfExists(`./assets/${file}.plist`);
const pngFile = await returnFileIfExists(`./assets/${file}.png`);
if (xmlFile == null || pngFile == null) {
console.log("File not found");
Deno.exit();
}
const parsed = parse(xmlFile) as GDPlist;
const sheet = await Image.decode(pngFile);
await Deno.mkdir(`./out/${file}`, { recursive: true });
// Just to make it easier to pass in .map
const parseNumber = (str: string) => parseInt(str);
let i = 1;
const assets = Object.keys(parsed.frames).length;
for (const [name, data] of Object.entries(parsed.frames)) {
console.log(`Processing ${name} ${i++}/${assets}`)
const [spriteX, spriteY] = data.spriteSize
.replace(/[{}]/g, "")
.split(",")
.map(parseNumber);
const sprite = new Image(spriteX, spriteY);
const [textureX, textureY, textureW, textureH] = data.textureRect
.replace(/[{}]/g, "")
.split(",")
.map(parseNumber);
const clonedSheet = sheet.clone();
if (data.textureRotated) {
sprite.resize(spriteY, spriteX);
sprite.composite(clonedSheet.crop(textureX, textureY, textureH, textureW));
sprite.rotate(90, true)
} else {
sprite.composite(clonedSheet.crop(textureX, textureY, textureW, textureH));
}
await Deno.writeFile(`./out/${file}/${name}`, await sprite.encode());
}
console.log("Done");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment