mirror of
				https://github.com/pagefaultgames/pokerogue.git
				synced 2025-10-31 16:35:52 +01:00 
			
		
		
		
	* Add decrypt-save.js * Update scripts/decrypt-save.js Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com> --------- Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com>
		
			
				
	
	
		
			150 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			150 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import pkg from "crypto-js";
 | |
| const { AES, enc } = pkg;
 | |
| // biome-ignore lint: This is how you import fs from node
 | |
| import * as fs from "node:fs";
 | |
| 
 | |
| const SAVE_KEY = "x0i2O7WRiANTqPmZ";
 | |
| 
 | |
| /**
 | |
|  * A map of condensed keynames to their associated full names
 | |
|  * NOTE: Update this if `src/system/game-data#systemShortKeys` ever changes!
 | |
|  */
 | |
| const systemShortKeys = {
 | |
|   seenAttr: "$sa",
 | |
|   caughtAttr: "$ca",
 | |
|   natureAttr: "$na",
 | |
|   seenCount: "$s",
 | |
|   caughtCount: "$c",
 | |
|   hatchedCount: "$hc",
 | |
|   ivs: "$i",
 | |
|   moveset: "$m",
 | |
|   eggMoves: "$em",
 | |
|   candyCount: "$x",
 | |
|   friendship: "$f",
 | |
|   abilityAttr: "$a",
 | |
|   passiveAttr: "$pa",
 | |
|   valueReduction: "$vr",
 | |
|   classicWinCount: "$wc",
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Replace the shortened key names with their full names
 | |
|  * @param {string} dataStr - The string to convert
 | |
|  * @returns {string} The string with shortened keynames replaced with full names
 | |
|  */
 | |
| function convertSystemDataStr(dataStr) {
 | |
|   const fromKeys = Object.values(systemShortKeys);
 | |
|   const toKeys = Object.keys(systemShortKeys);
 | |
|   for (const k in fromKeys) {
 | |
|     dataStr = dataStr.replace(new RegExp(`${fromKeys[k].replace("$", "\\$")}`, "g"), toKeys[k]);
 | |
|   }
 | |
| 
 | |
|   return dataStr;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Decrypt a save
 | |
|  * @param {string} path - The path to the encrypted save file
 | |
|  * @returns {string} The decrypted save data
 | |
|  */
 | |
| function decryptSave(path) {
 | |
|   // Check if the file exists
 | |
|   if (!fs.existsSync(path)) {
 | |
|     console.error(`File not found: ${path}`);
 | |
|     process.exit(1);
 | |
|   }
 | |
|   let fileData;
 | |
|   try {
 | |
|     fileData = fs.readFileSync(path, "utf8");
 | |
|   } catch (e) {
 | |
|     switch (e.code) {
 | |
|       case "ENOENT":
 | |
|         console.error(`File not found: ${path}`);
 | |
|         break;
 | |
|       case "EACCES":
 | |
|         console.error(`Could not open ${path}: Permission denied`);
 | |
|         break;
 | |
|       case "EISDIR":
 | |
|         console.error(`Unable to read ${path} as it is a directory`);
 | |
|         break;
 | |
|       default:
 | |
|         console.error(`Error reading file: ${e.message}`);
 | |
|     }
 | |
|     process.exit(1);
 | |
|   }
 | |
|   return convertSystemDataStr(AES.decrypt(fileData, SAVE_KEY).toString(enc.Utf8));
 | |
| }
 | |
| 
 | |
| /* Print the usage message and exits */
 | |
| function printUsage() {
 | |
|   console.log(`
 | |
| Usage: node decrypt-save.js <encrypted-file> [save-file]
 | |
| 
 | |
| Arguments:
 | |
|   file-path   Path to the encrypted save file to decrypt.
 | |
|   save-file   Path to where the decrypted data should be written. If not provided, the decrypted data will be printed to the console.
 | |
| 
 | |
| Options:
 | |
|   -h, --help  Show this help message and exit.
 | |
| 
 | |
| Description:
 | |
|   This script decrypts an encrypted pokerogue save file
 | |
| `);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Write `data` to `filePath`, gracefully communicating errors that arise
 | |
|  * @param {string} filePath
 | |
|  * @param {string} data
 | |
|  */
 | |
| function writeToFile(filePath, data) {
 | |
|   try {
 | |
|     fs.writeFileSync(filePath, data);
 | |
|   } catch (e) {
 | |
|     switch (e.code) {
 | |
|       case "EACCES":
 | |
|         console.error(`Could not open ${filePath}: Permission denied`);
 | |
|         break;
 | |
|       case "EISDIR":
 | |
|         console.error(`Unable to write to ${filePath} as it is a directory`);
 | |
|         break;
 | |
|       default:
 | |
|         console.error(`Error writing file: ${e.message}`);
 | |
|     }
 | |
|     process.exit(1);
 | |
|   }
 | |
| }
 | |
| 
 | |
| function main() {
 | |
|   let args = process.argv.slice(2);
 | |
|   // Get options
 | |
|   const options = args.filter(arg => arg.startsWith("-"));
 | |
|   // get args
 | |
|   args = args.filter(arg => !arg.startsWith("-"));
 | |
| 
 | |
|   if (args.length === 0 || options.includes("-h") || options.includes("--help") || args.length > 2) {
 | |
|     printUsage();
 | |
|     process.exit(0);
 | |
|   }
 | |
|   // If the user provided a second argument, check if the file exists already and refuse to write to it.
 | |
|   if (args.length === 2) {
 | |
|     const destPath = args[1];
 | |
|     if (fs.existsSync(destPath)) {
 | |
|       console.error(`Refusing to overwrite ${destPath}`);
 | |
|       process.exit(1);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Otherwise, commence decryption.
 | |
|   const decrypt = decryptSave(args[0]);
 | |
| 
 | |
|   if (args.length === 1) {
 | |
|     process.stdout.write(decrypt);
 | |
|     process.exit(0);
 | |
|   }
 | |
| 
 | |
|   writeToFile(destPath, decrypt);
 | |
| }
 | |
| 
 | |
| main();
 |