@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
482 lines (481 loc) • 16.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const StorageUtilities_1 = __importDefault(require("../storage/StorageUtilities"));
const path = __importStar(require("path"));
const Utilities_1 = __importDefault(require("../core/Utilities"));
const electron_1 = require("electron");
const Log_1 = __importDefault(require("../core/Log"));
const allowedExtensions = [
"js",
"ts",
"mjs",
"json",
"md",
"png",
"jpg",
"jpeg",
"lang",
"fsb",
"map",
"ogg",
"flac",
"hdr",
"psd",
"env",
"gif",
"wav",
"tga",
"env",
"properties",
"cartobackup",
"mctbackup",
"wlist",
"brarchive",
"nbt",
"webm",
"svg",
"otf",
"ttf",
"bin",
"obj",
"pdn",
"zip",
"h",
"py",
"fontdata",
"",
"mcstructure",
"mcworld",
"mcproject",
"map",
"js.map",
"mctemplate",
"material",
"vertex",
"md",
"geometry",
"fragment",
"mcfunction",
"mcaddon",
"mcpack",
"html",
"dat",
"dat_old",
"txt",
"ldb",
"log",
"in",
"cmake",
];
const allowedExecutableExtensions = ["mcstructure", "mcworld", "mctemplate", "mcaddon"];
class ElectronUtils {
// Static set to track tokens we've already warned about across all instances
static _warnedMissingTokens = new Set();
_minecraftPreviewPath;
_minecraftReleasePath;
_minecraftEducationPreviewPath;
_minecraftEducationReleasePath;
_minecraftPEPath;
_uniqueIdToPathMappings;
_pathToUniqueIdMappings;
_env;
_docFolderEnsured = false;
constructor(env) {
this._env = env;
this._minecraftPreviewPath = undefined;
this._minecraftReleasePath = undefined;
this._uniqueIdToPathMappings = {};
if (this._env.pathMappings) {
this._pathToUniqueIdMappings = this._env.pathMappings;
for (const path in this._pathToUniqueIdMappings) {
const uniqueId = this._pathToUniqueIdMappings[path];
this._uniqueIdToPathMappings[uniqueId] = path;
}
}
else {
this._pathToUniqueIdMappings = {};
}
}
ensureMappingForPath(path) {
path = path.replace(/\\/g, "/");
let pathCanon = StorageUtilities_1.default.canonicalizePath(path);
if (pathCanon.indexOf("/") >= 0 && !pathCanon.endsWith("/")) {
pathCanon += "/";
}
if (this._pathToUniqueIdMappings[pathCanon]) {
return this._pathToUniqueIdMappings[pathCanon];
}
else {
const uniqueId = StorageUtilities_1.default.getLeafName(path) + "-" + Utilities_1.default.createRandomLowerId(6);
this._pathToUniqueIdMappings[pathCanon] = uniqueId;
this._uniqueIdToPathMappings[uniqueId] = pathCanon;
this._env.pathMappings = this._pathToUniqueIdMappings;
this._env.save();
return uniqueId;
}
}
hasMappingForPath(path) {
const pathCanon = StorageUtilities_1.default.canonicalizePath(path);
if (this._pathToUniqueIdMappings[pathCanon]) {
return true;
}
return false;
}
getMinecraftReleasePath() {
if (this._minecraftReleasePath) {
return this._minecraftReleasePath;
}
this._minecraftReleasePath =
this.getRoamingPath() +
"Microsoft Bedrock" +
path.sep +
"Users" +
path.sep +
"Shared" +
path.sep +
"games" +
path.sep +
"com.mojang" +
path.sep;
// fall back to older UWP path
if (!fs.existsSync(this._minecraftReleasePath)) {
this._minecraftReleasePath =
this.getUserLocalPath() +
"Packages" +
path.sep +
"Microsoft.MinecraftUWP_8wekyb3d8bbwe" +
path.sep +
"LocalState" +
path.sep +
"games" +
path.sep +
"com.mojang" +
path.sep;
if (!fs.existsSync(this._minecraftReleasePath)) {
this._minecraftReleasePath = undefined;
}
}
return this._minecraftReleasePath;
}
getMinecraftPreviewPath() {
if (this._minecraftPreviewPath) {
return this._minecraftPreviewPath;
}
this._minecraftPreviewPath =
this.getRoamingPath() +
"Microsoft Bedrock Preview" +
path.sep +
"Users" +
path.sep +
"Shared" +
path.sep +
"games" +
path.sep +
"com.mojang" +
path.sep;
// fall back to older UWP path
if (!fs.existsSync(this._minecraftPreviewPath)) {
this._minecraftPreviewPath =
this.getUserLocalPath() +
"Packages" +
path.sep +
"Microsoft.MinecraftWindowsBeta_8wekyb3d8bbwe" +
path.sep +
"LocalState" +
path.sep +
"games" +
path.sep +
"com.mojang" +
path.sep;
if (!fs.existsSync(this._minecraftPreviewPath)) {
this._minecraftPreviewPath = undefined;
}
}
return this._minecraftPreviewPath;
}
getMinecraftEducationReleasePath() {
if (this._minecraftEducationReleasePath) {
return this._minecraftEducationReleasePath;
}
this._minecraftEducationReleasePath =
this.getUserLocalPath() +
"Packages" +
path.sep +
"Microsoft.MinecraftEducationEdition_8wekyb3d8bbwe" +
path.sep +
"LocalState" +
path.sep +
"games" +
path.sep +
"com.mojang" +
path.sep;
return this._minecraftEducationReleasePath;
}
getMinecraftEducationPreviewPath() {
if (this._minecraftEducationPreviewPath) {
return this._minecraftEducationPreviewPath;
}
this._minecraftEducationPreviewPath =
this.getUserLocalPath() +
"Packages" +
path.sep +
"Microsoft.MinecraftEducationPreview_8wekyb3d8bbwe" +
path.sep +
"LocalState" +
path.sep +
"games" +
path.sep +
"com.mojang" +
path.sep;
return this._minecraftEducationPreviewPath;
}
getMinecraftPEPath() {
if (this._minecraftPEPath) {
return this._minecraftPEPath;
}
this._minecraftPEPath =
this.getRoamingPath() + "MicrosoftPE" + path.sep + "games" + path.sep + "com.mojang" + path.sep;
return this._minecraftPEPath;
}
deTokenizePath(untrustedPath) {
let resultPath = undefined;
if (untrustedPath.startsWith("<BDPV>")) {
let udPath = this.getMinecraftPreviewPath();
if (!udPath) {
return undefined;
}
let segment = untrustedPath.substring(6);
if (segment.startsWith(path.sep)) {
segment = segment.substring(1);
}
resultPath = udPath + segment;
}
else if (untrustedPath.startsWith("<BDRK>")) {
let udPath = this.getMinecraftReleasePath();
if (!udPath) {
return undefined;
}
let segment = untrustedPath.substring(6);
if (segment.startsWith(path.sep)) {
segment = segment.substring(1);
}
resultPath = udPath + segment;
}
else if (untrustedPath.startsWith("<EDUR>")) {
let udPath = this.getMinecraftEducationReleasePath();
if (!udPath) {
return undefined;
}
let segment = untrustedPath.substring(6);
if (segment.startsWith(path.sep)) {
segment = segment.substring(1);
}
resultPath = udPath + segment;
}
else if (untrustedPath.startsWith("<EDUP>")) {
let udPath = this.getMinecraftEducationPreviewPath();
if (!udPath) {
return undefined;
}
let segment = untrustedPath.substring(6);
if (segment.startsWith(path.sep)) {
segment = segment.substring(1);
}
resultPath = udPath + segment;
}
else if (untrustedPath.startsWith("<MCPE>")) {
let udPath = this.getMinecraftPEPath();
if (!udPath) {
return undefined;
}
let segment = untrustedPath.substring(6);
if (segment.startsWith(path.sep)) {
segment = segment.substring(1);
}
resultPath = udPath + segment;
}
else if (untrustedPath.startsWith("<DOCP>")) {
// Check if test storage override is set (for automated tests)
const testStorageRoot = process.env.MCT_TEST_STORAGE_ROOT;
let docPBasePath;
if (testStorageRoot) {
// Use test storage root instead of user's Documents folder
docPBasePath = testStorageRoot.endsWith(path.sep) ? testStorageRoot : testStorageRoot + path.sep;
}
else {
// Default: use Documents folder
docPBasePath = electron_1.app.getPath("documents") + path.sep + "Minecraft Creator Tools" + path.sep;
}
let segment = untrustedPath.substring(6);
if (segment.startsWith(path.sep)) {
segment = segment.substring(1);
}
if (!this._docFolderEnsured) {
this._docFolderEnsured = true;
if (!fs.existsSync(docPBasePath)) {
fs.mkdirSync(docPBasePath, { recursive: true });
}
}
resultPath = docPBasePath + segment;
}
else if (untrustedPath.startsWith("<pt_")) {
resultPath = this.fixupTokenPath(untrustedPath);
}
if (resultPath) {
return resultPath.replace(/\//g, path.sep);
}
return undefined;
}
fixupTokenPath(path) {
path = path.replace(/\\/g, "/");
if (path.startsWith("<pt_")) {
let nextGreater = path.indexOf(">");
if (nextGreater >= 0) {
const tok = path.substring(4, nextGreater);
let tokenPath = this.getTokenPath(tok);
if (tokenPath) {
let nextSeg = path.substring(nextGreater + 1);
if (tokenPath.indexOf("/") >= 0 && !tokenPath.endsWith("/")) {
tokenPath += "/";
}
if (tokenPath.endsWith("/") && nextSeg.startsWith("/")) {
nextSeg = nextSeg.substring(1);
}
return tokenPath + nextSeg;
}
else {
// Only warn once per missing token to reduce log noise (use static to dedupe across instances)
if (!ElectronUtils._warnedMissingTokens.has(tok)) {
ElectronUtils._warnedMissingTokens.add(tok);
Log_1.default.debugAlert("Could not find path mapping for token '" +
tok +
"'. Project may reference a folder from a previous session.");
}
}
}
}
return undefined;
}
getTokenPath(token) {
return this._uniqueIdToPathMappings[token];
}
validateFolderPath(path) {
// banned character combos
if (path.indexOf("..") >= 0 || path.indexOf("\\\\") >= 0 || path.indexOf("//") >= 0) {
throw new Error("Unsupported path combinations: " + path);
}
if (path.lastIndexOf(":") >= 2) {
throw new Error("Unsupported drive location: " + path);
}
let count = this.countChar(path, "\\") + this.countChar(path, "/");
if (!path.endsWith("\\") && !path.endsWith("/")) {
count++;
}
if (count < 2) {
throw new Error("Unsupported base path: " + path);
}
}
validateFilePath(path) {
this.validateFolderPath(path);
const extension = this.getTypeFromName(path);
if (!allowedExtensions.includes(extension)) {
throw new Error("Unsupported file type: " + path);
}
}
validateExecutableFilePath(path) {
this.validateFolderPath(path);
const extension = this.getTypeFromName(path);
if (!allowedExecutableExtensions.includes(extension)) {
throw new Error("Unsupported executable file type: " + path);
}
}
getTypeFromName(name) {
const nameW = name.trim().toLowerCase();
const lastBackslash = nameW.lastIndexOf("\\");
const lastSlash = nameW.lastIndexOf("/");
const lastPeriod = nameW.lastIndexOf(".");
if (lastPeriod < 0 || lastPeriod < lastSlash || lastPeriod < lastBackslash) {
return "";
}
return nameW.substring(lastPeriod + 1, nameW.length);
}
arrayBufferToBase64(buffer) {
var binary = "";
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
base64ToArrayBuffer(base64buffer) {
const binary = atob(base64buffer);
const arrayBuffer = new ArrayBuffer(binary.length);
const bytes = new Uint8Array(arrayBuffer);
const len = binary.length;
for (let i = 0; i < len; i++) {
bytes[i] = binary.charCodeAt(i);
}
return arrayBuffer;
}
countChar(source, find) {
let count = 0;
let index = source.indexOf(find);
while (index >= 0) {
count++;
index = source.indexOf(find, index + find.length);
}
return count;
}
getRoamingPath() {
let udPath = electron_1.app.getPath("userData");
const lastSlash = udPath.lastIndexOf(path.sep);
if (lastSlash >= 0) {
udPath = udPath.substring(0, lastSlash + 1);
}
return udPath;
}
getUserLocalPath() {
let udPath = this.getRoamingPath();
udPath = udPath.replace("\\Roaming\\", "\\Local\\");
return udPath;
}
}
exports.default = ElectronUtils;