Created
June 5, 2025 23:32
-
-
Save widberg/c9c0692b963c8def7accdf51c0f9895b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # Monster Prom Save Editor | |
| # Usage: python3 monster_prom_save_editor.py decrypt 76561198083837418_GameProgress.xml 76561198083837418_GameProgress_decrypted.xml | |
| # Usage: python3 monster_prom_save_editor.py encrypt 76561198083837418_GameProgress_decrypted.xml 76561198083837418_GameProgress.xml | |
| # See https://www.pcgamingwiki.com/wiki/Monster_Prom for save file locations | |
| # Backup your save file before using this script | |
| import argparse | |
| import base64 | |
| import xml.etree.ElementTree as ET | |
| from Crypto.Cipher import AES | |
| from Crypto.Util.Padding import unpad, pad | |
| MONSTER_PROM_KEY = b"07372495761297179318254963745127" | |
| MONSTER_PROM_RIJNDAEL = AES.new(MONSTER_PROM_KEY, AES.MODE_ECB) | |
| MONSTER_PROM_ENCODING = "us-ascii" | |
| def decrypt(args): | |
| encrypted_file = args.encrypted_file | |
| decrypted_file = args.decrypted_file | |
| tree = ET.parse(encrypted_file) | |
| root = tree.getroot() | |
| ciphertext = base64.b64decode(root.text) | |
| plaintext = unpad( | |
| MONSTER_PROM_RIJNDAEL.decrypt(ciphertext), AES.block_size, style="pkcs7" | |
| ) | |
| inner_tree = ET.fromstringlist(["<root>", plaintext, "</root>"]) | |
| root.clear() | |
| root.extend(inner_tree) | |
| new_tree = ET.ElementTree(root) | |
| ET.indent(new_tree) | |
| with open(encrypted_file, "wb") as f: | |
| # Use double quotes and \r\n in the declaration | |
| f.write( | |
| f'<?xml version="1.0" encoding="{MONSTER_PROM_ENCODING}"?>\r\n'.encode( | |
| "ascii" | |
| ) | |
| ) | |
| new_tree.write(f, encoding=MONSTER_PROM_ENCODING, xml_declaration=False) | |
| def strip_whitespace(elem): | |
| if elem.text: | |
| elem.text = elem.text.strip() | |
| if elem.tail: | |
| elem.tail = elem.tail.strip() | |
| for child in elem: | |
| strip_whitespace(child) | |
| def encrypt(args): | |
| decrypted_file = args.decrypted_file | |
| encrypted_file = args.encrypted_file | |
| tree = ET.parse(decrypted_file) | |
| root = tree.getroot() | |
| strip_whitespace(root) | |
| plaintext = b"".join( | |
| ET.tostring(child, encoding=MONSTER_PROM_ENCODING) for child in root | |
| ) | |
| ciphertext = MONSTER_PROM_RIJNDAEL.encrypt( | |
| pad(plaintext, AES.block_size, style="pkcs7") | |
| ) | |
| root.clear() | |
| root.text = base64.b64encode(ciphertext).decode("ascii") | |
| new_tree = ET.ElementTree(root) | |
| with open(encrypted_file, "wb") as f: | |
| # Use double quotes and \r\n in the declaration | |
| f.write( | |
| f'<?xml version="1.0" encoding="{MONSTER_PROM_ENCODING}"?>\r\n'.encode( | |
| "ascii" | |
| ) | |
| ) | |
| new_tree.write(f, encoding=MONSTER_PROM_ENCODING, xml_declaration=False) | |
| def main(): | |
| parser = argparse.ArgumentParser(description="Monster Prom save editor") | |
| subparsers = parser.add_subparsers(dest="command", required=True) | |
| parser_decrypt = subparsers.add_parser("decrypt") | |
| parser_decrypt.add_argument("encrypted_file", type=str) | |
| parser_decrypt.add_argument("decrypted_file", type=str) | |
| parser_decrypt.set_defaults(func=decrypt) | |
| parser_encrypt = subparsers.add_parser("encrypt") | |
| parser_encrypt.add_argument("decrypted_file", type=str) | |
| parser_encrypt.add_argument("encrypted_file", type=str) | |
| parser_encrypt.set_defaults(func=encrypt) | |
| args = parser.parse_args() | |
| args.func(args) | |
| if __name__ == "__main__": | |
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| pycryptodome==3.23.0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment