diff --git a/package-lock.json b/package-lock.json index 856bc83..8ef9aef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "mongoose": "~7.6.1", "morgan": "^1.10.0", "nice-grpc": "^2.1.10", + "xml-js": "^1.6.11", "xmlbuilder": "^15.1.1" }, "devDependencies": { @@ -7447,6 +7448,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -8695,6 +8701,17 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", @@ -14034,6 +14051,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, "semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -14914,6 +14936,14 @@ "strip-ansi": "^6.0.0" } }, + "xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "requires": { + "sax": "^1.2.4" + } + }, "xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", diff --git a/package.json b/package.json index 8f7484b..f8e3c94 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "mongoose": "~7.6.1", "morgan": "^1.10.0", "nice-grpc": "^2.1.10", + "xml-js": "^1.6.11", "xmlbuilder": "^15.1.1" }, "devDependencies": { diff --git a/update-rotation.mjs b/update-rotation.mjs new file mode 100644 index 0000000..69cb47e --- /dev/null +++ b/update-rotation.mjs @@ -0,0 +1,114 @@ +/* eslint-disable no-undef -- Tis a script */ + +import path from 'path'; +import crypto from 'crypto'; +import readline from 'readline'; +import BOSS from 'boss-js'; +import fs from 'fs-extra'; +import dotenv from 'dotenv'; +import xml from 'xml-js'; + +dotenv.config(); + +const { PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY, PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY } = process.env; + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); + +const askQuestion = (question) => { + return new Promise((resolve) => { + rl.question(question, (answer) => { + resolve(answer); + }); + }); +}; + +const rootDir = import.meta.dirname; +const sourceFile = path.join(rootDir, 'VSSetting.byaml'); + +const exists = await fs.exists(sourceFile); +if (!exists) { + throw new Error('Source VSSetting.byaml file does not exist'); +} + +const stat = await fs.stat(sourceFile); +if (stat.mtime.toDateString() !== new Date().toDateString()) { + const answer = await askQuestion(`The source file was not updated today (Updated ${stat.mtime.toDateString()}). Do you want to continue? (y/n) `); + if (answer.toLowerCase() !== 'y') { + process.exit(0); + } +} + +const titles = [ + 'bb6tOEckvgZ50ciH', + 'rjVlM7hUXPxmYQJh', + 'zvGSM4kOrXpkKnpT' +]; + +async function backupFile(filePath) { + const copyFilePath = path.join(path.dirname(filePath), `${path.basename(filePath)}.bak`); + await fs.copyFile(filePath, copyFilePath); + console.log(`Backup created of ${filePath} at ${copyFilePath}`); +} + +for (const title of titles) { + console.log(`\n --- Processing ${title} ---`); + const decryptedDir = path.join(rootDir, `cdn/content/decrypted/${title}`); + const encryptedDir = path.join(rootDir, `cdn/content/encrypted/${title}`); + const taskSheetDir = path.join(rootDir, `cdn/tasksheet/1/${title}`); + await fs.ensureDir(decryptedDir); + await fs.ensureDir(encryptedDir); + await fs.ensureDir(taskSheetDir); + + const decryptedFilePath = path.join(decryptedDir, 'VSSetting.byaml'); + await backupFile(decryptedFilePath); + + const encryptedContents = BOSS.encryptWiiU(decryptedFilePath, PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY, PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY); + const hash = crypto.createHash('md5').update(encryptedContents).digest('hex'); + console.log(`Hash for title ${title} is ${hash}`); + + const encryptedFilePath = path.join(encryptedDir, hash); + await fs.writeFile(encryptedFilePath, encryptedContents); + console.log(`Encrypted file created at ${encryptedFilePath}`); + + const taskSheetFilePath = path.join(taskSheetDir, 'schdat2'); + await backupFile(taskSheetFilePath); + + const tasksheetContents = await fs.readFile(taskSheetFilePath, 'utf8'); + const xmlContents = xml.xml2js(tasksheetContents, { compact: true }); + + const dataId = parseInt(xmlContents.TaskSheet.Files.File.DataId._text); + if (isNaN(dataId)) { + console.error(`DataId for title ${title} is not a number, skipping...`); + continue; + } + console.log(`DataId for title ${title} is ${dataId}`); + const newDataId = dataId + 1; + console.log(`New DataId for title ${title} is ${newDataId}`); + + xmlContents.TaskSheet.Files.File.DataId._text = newDataId.toString(); + + const size = encryptedContents.length; + const oldSize = parseInt(xmlContents.TaskSheet.Files.File.Size._text); + if (size === oldSize) { + console.log(`Size for title ${title} is already updated, skipping update...`); + } else { + console.log(`Old size for title ${title} is ${oldSize}`); + console.log(`New size for title ${title} is ${size}`); + xmlContents.TaskSheet.Files.File.Size._text = size.toString(); + } + + const oldUrl = xmlContents.TaskSheet.Files.File.Url._text; + const newUrl = `https://npdi.cdn.pretendo.cc/p01/data/1/${title}/${newDataId}/${hash}`; + console.log(`Old URL for title ${title} is ${oldUrl}`); + console.log(`New URL for title ${title} is ${newUrl}`); + xmlContents.TaskSheet.Files.File.Url._text = newUrl; + + const newXmlContents = xml.js2xml(xmlContents, { spaces: 2, compact: true }); + await fs.writeFile(taskSheetFilePath, newXmlContents); + console.log(`Tasksheet file updated at ${taskSheetFilePath}`); +} + +rl.close();