bedrock-launcher-version-symlink-generator
Version:
A CLI to create symlinks for each of the version folders in Bedrock Launcher, so you can access the folders by version number instead of by UUID.
92 lines (83 loc) • 4.27 kB
text/typescript
import chalk from "chalk";
import { exec, type ExecException } from "child_process";
import { existsSync, mkdirSync, readdirSync, readFileSync } from "fs";
import path from "path";
/**
* The path to the user folder.
*
* This is the same folder you are sent to if your type `%AppData%/../../` in the `WIN+R` Run dialog.
*/
export const userFolderPath: string = import.meta.dirname.split(path.sep).slice(0, 3).join(path.sep);
// Check if the user folder path is valid.
if (!new RegExp(`[A-Z]:${path.sep === "\\" ? "\\\\" : path.sep}Users${path.sep === "\\" ? "\\\\" : path.sep}`).test(userFolderPath)) {
console.error(chalk.red(`Invalid user folder path, please make sure this package is installed globally: "${userFolderPath}"`));
process.exit(1);
}
/**
* The path to the Bedrock Launcher data folder.
*/
export const mcBedrockFolderPath: string = path.join(userFolderPath, "AppData/Roaming/.minecraft_bedrock");
// Check if Bedrock Launcher is installed.
if (!existsSync(mcBedrockFolderPath)) {
console.error(chalk.red(`Bedrock Launcher is not installed.`));
process.exit(1);
}
/**
* The path to the Bedrock Launcher versions folder.
*/
const mcBedrockVersionsFolderPath: string = path.join(mcBedrockFolderPath, "versions");
/**
* The list of folders for all the currently installed Minecraft Bedrock Edition versions.
*/
const bedrockLauncherVersionFolders: string[] = readdirSync(mcBedrockVersionsFolderPath).filter((versionFolder) => versionFolder.split("-").length === 5);
/**
* The list of version numbers.
*/
const versionNumbers: (`${number}.${number}.${number}.${number}_${"Release" | "Preview"}` | "Unable to determine version.")[] =
bedrockLauncherVersionFolders.map((versionFolder) => {
const AppxManifestXML: string = path.join(mcBedrockVersionsFolderPath, versionFolder, "AppxManifest.xml");
const AppxManifestXMLContent: string = readFileSync(AppxManifestXML, "utf-8");
const AppxManifestXMLVersion: string | undefined = AppxManifestXMLContent.match(
/\<Identity Name="(?:Microsoft\.MinecraftUWP|Microsoft\.MinecraftWindowsBeta)" Publisher="[^"]*" Version="([\d\.]+)"/
)?.[1];
if (!AppxManifestXMLVersion) {
return "Unable to determine version." as const;
}
const AppxManifestXMLEdition: "Minecraft for Windows" | "Minecraft Windows Preview" | undefined = AppxManifestXMLContent.match(
/\<DisplayName\>(Minecraft for Windows|Minecraft Windows Preview)\<\/DisplayName>/
)?.[1] as "Minecraft for Windows" | "Minecraft Windows Preview" | undefined;
const versionSegments = AppxManifestXMLVersion.split(".");
return `${Number(versionSegments[0])}.${Number(versionSegments[1])}.${Number(versionSegments[2]?.slice(0, -2))}.${Number(
versionSegments[2]?.slice(-2)
)}_${AppxManifestXMLEdition === "Minecraft for Windows" ? "Release" : "Preview"}` as const;
});
mkdirSync(path.join(mcBedrockFolderPath, "versionSymlinks"), { recursive: true });
for (let i = 0; i < bedrockLauncherVersionFolders.length; i++) {
if (versionNumbers[i] === "Unable to determine version.") {
console.warn(chalk.yellow(`Unable to determine version for folder "${bedrockLauncherVersionFolders[i]}". Skipping...`));
continue;
}
await runCommmand(
`mklink /J "${path.join(mcBedrockFolderPath, "versionSymlinks", versionNumbers[i]!)}" "${path.join(
mcBedrockVersionsFolderPath,
bedrockLauncherVersionFolders[i]!
)}"`
).then((r) => {
if (r.err !== null) {
throw r.err;
}
});
}
/**
* Runs a command.
*
* @param {string} command The command to run.
* @returns A promise that resolves with the results of the command.
*/
export async function runCommmand(command: string): Promise<{ err: ExecException | null; stdout: string; stderr: string }> {
return new Promise((resolve: (value: { err: ExecException | null; stdout: string; stderr: string }) => void) => {
exec(command, (err: ExecException | null, stdout: string, stderr: string) => {
resolve({ err, stdout, stderr });
});
});
}