MCPackDecrypt/main.js

158 lines
4.8 KiB
JavaScript

const {app, BrowserWindow, ipcMain, dialog} = require("electron");
const path = require("path");
const fs = require("fs");
const PackDecryptor = require("./packDecrypter");
const minecraftFolderPath = path.join(process.env.LocalAppData, "/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe");
const localStatePath = path.join(minecraftFolderPath, "LocalState")
const premiumCachePath = path.join(localStatePath, "premium_cache");
const worldsPath = path.join(localStatePath, "/games/com.mojang/minecraftWorlds");
function checkWorldEncrypted(worldPath){
const worldDbFolder = path.join(worldPath, "db");
const dbFiles = fs.readdirSync(worldDbFolder);
for(let index = 0; index < dbFiles.length; index++) {
if(path.extname(dbFiles[index]).toLowerCase() === ".ldb") {
return PackDecryptor.isContentFileEncrypted(path.join(worldDbFolder, dbFiles[index]))
}
}
return false;
}
function getWorlds() {
const worlds = [];
if(fs.existsSync(worldsPath)) {
const files = fs.readdirSync(worldsPath);
for (let index = 0; index < files.length; index++) {
const isEncrypted = checkWorldEncrypted(path.join(worldsPath, files[index]));
if (!isEncrypted) continue;
const name = fs.readFileSync(path.join(worldsPath, files[index], 'levelname.txt'), 'utf8');
const packIcon = getPackIcon(path.join(worldsPath, files[index]));
worlds.push({
name: replaceName(name),
packPath: path.join(worldsPath, files[index]),
packIcon,
})
}
}
return worlds;
}
function getPremiumCache() {
const packTypes = {};
if(fs.existsSync(premiumCachePath)) {
const files = fs.readdirSync(premiumCachePath);
for (let index = 0; index < files.length; index++) {
const dirname = files[index];
packTypes[dirname] = [];
const packs = getPacks(path.join(premiumCachePath, dirname))
if (packs.length === 0) {
delete packTypes[dirname];
continue;
}
packTypes[dirname] = packs;
}
}
return packTypes
}
function getPacks(dirPath) {
const packList = fs.readdirSync(dirPath);
return packList.map(packDir => {
const packPath = path.join(dirPath, packDir);
const packName = getPackName(packPath)
return {
name: replaceName(packName),
packPath: packPath,
packIcon: getPackIcon(packPath),
}
})
}
function replaceName(name) {
return name
.replaceAll("#", "")
.replaceAll("?", "")
.replaceAll("*", "")
.replaceAll("<", "")
.replaceAll(">", "")
.replaceAll("|", "")
.replaceAll(":", "")
.replaceAll("\\", "")
.replaceAll("/", "")
.trim()
}
function getPackIcon(packPath) {
const packIconNames = ["pack_icon.png", "pack_icon.jpeg" ,"world_icon.jpeg", "world_icon.png"]
for (let index = 0; index < packIconNames.length; index++) {
const packIconName = packIconNames[index];
const iconPath = path.join(packPath, packIconName);
if (fs.existsSync(iconPath)) {
return fs.readFileSync(iconPath, 'base64')
}
}
return null;
}
function getPackName(packPath) {
const langFile = fs.readFileSync(path.join(packPath, "texts", "en_US.lang"), 'utf8');
return langFile.split("\n")[0].split("=").at(-1).replace("\n", "").replace("\r", "");
}
app.whenReady().then(() => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegrationInWorker: true,
}
});
win.removeMenu()
win.loadFile("./renderer/index.html")
//win.webContents.openDevTools({mode: 'detach'})
ipcMain.handle("get-packs", (event) => {
packs = {worlds: getWorlds(), ...getPremiumCache()};
if(packs["worlds"].length == 0)
delete packs["worlds"];
return packs;
})
ipcMain.handle("pick-path", async (event, {path, type, name}) => {
const filter = {};
if (type === "world_templates") {
filter.name = "World Template";
filter.extensions = ["mctemplate"]
}
if (type === "resource_packs") {
filter.name = "Resource Pack";
filter.extensions = ["mcpack"]
}
if (type === "skin_packs") {
filter.name = "Skin Pack";
filter.extensions = ["mcpack"]
}
if (type === "persona") {
filter.name = "Persona Peice";
filter.extensions = ["mcpersona"]
}
if (type === "worlds") {
filter.name = "World";
filter.extensions = ["mcworld"]
}
const dialogReturnValue = await dialog.showSaveDialog({
defaultPath: name,
filters: [filter]
})
if (dialogReturnValue.canceled) return;
return dialogReturnValue.filePath;
})
})