/* 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(); function md5(input) { return crypto.createHash('md5').update(input).digest('hex'); } const BOSS_WIIU_AES_KEY_MD5_HASH = '5202ce5099232c3d365e28379790a919'; const BOSS_WIIU_HMAC_KEY_MD5_HASH = 'b4482fef177b0100090ce0dbeb8ce977'; const { PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY, PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY } = process.env; if (!PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY || md5(PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY) !== BOSS_WIIU_AES_KEY_MD5_HASH) { console.error('PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY is not set or does not match the expected value'); process.exit(1); } if (!PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY || md5(PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY) !== BOSS_WIIU_HMAC_KEY_MD5_HASH) { console.error('PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY is not set or does not match the expected value'); process.exit(1); } 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 checksumFile = path.join(rootDir, 'cdn/VSSetting.byaml.checksum'); const exists = await fs.exists(sourceFile); if (!exists) { console.error('Source VSSetting.byaml file does not exist'); process.exit(1); } const sourceFileContents = await fs.readFile(sourceFile); 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 checksumExists = await fs.exists(checksumFile); const newChecksum = crypto.createHash('sha256').update(sourceFileContents).digest('hex'); console.log(`Checksum for source file is ${newChecksum}`); if (checksumExists) { const checksum = await fs.readFile(checksumFile, 'utf8'); if (checksum === newChecksum) { const answer = await askQuestion('The source file has not changed since the last run. 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`); const exists = await fs.exists(filePath); if (!exists) { console.log(`File ${filePath} does not exist, skipping backup...`); return; } 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); await fs.copyFile(sourceFile, 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(); console.log('All tasks completed successfully!'); await fs.writeFile(checksumFile, newChecksum);