Advertisement
jh0ker

max_prisoner_no_input.py

May 6th, 2022 (edited)
390
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.58 KB | None | 0 0
  1. """
  2. This script manipulates the SCUM database for the single player mode to
  3. increase the level of all skills and attributes to max (or the value of
  4. your choice).
  5.  
  6. Edit the constants below to change the target values for the skills and
  7. attributes. Default is maxed out.
  8.  
  9. Tested with Python 3.10 on May 6th, 2022 with SCUM Build 0.7.5.46714
  10. """
  11.  
  12.  
  13. from dataclasses import dataclass
  14. import datetime as dt
  15. import os
  16. from pathlib import Path
  17. import shutil
  18. import sqlite3
  19. import struct
  20. import traceback
  21. from typing import Literal
  22.  
  23. #### Configuration ####
  24.  
  25. PRISONER_ID = 123
  26.  
  27. ## Main attributes ##
  28. SET_ATTRIBUTES = {
  29.     "BaseStrength": 8.0,  # 1.0 to 8.0
  30.     "BaseConstitution": 5.0,  # 1.0 to 5.0
  31.     "BaseDexterity": 5.0,  # 1.0 to 5.0
  32.     "BaseIntelligence": 5.0,  # 1.0 to 5.0
  33. }
  34.  
  35. ## Skills ##
  36. """
  37. You can remove skills from the lists below and they will not be changed.
  38. If a new skill is added to the game, you can add it to the lists below.
  39.  
  40. The first number in each line is the skill level (0 - 3)
  41. The second number is the skill experience (0 - 10000000)
  42. """
  43.  
  44. SET_SKILLS = {
  45.     "BoxingSkill": (3, 10000000),
  46.     "AwarenessSkill": (3, 10000000),
  47.     "RiflesSkill": (3, 10000000),
  48.     "SnipingSkill": (3, 10000000),
  49.     "CamouflageSkill": (3, 10000000),
  50.     "SurvivalSkill": (3, 10000000),
  51.     "MeleeWeaponsSkill": (3, 10000000),
  52.     "HandgunSkill": (3, 10000000),
  53.     "RunningSkill": (3, 10000000),
  54.     "EnduranceSkill": (3, 10000000),
  55.     "TacticsSkill": (3, 10000000),
  56.     "CookingSkill": (3, 10000000),
  57.     "ThieverySkill": (3, 10000000),
  58.     "ArcherySkill": (3, 10000000),
  59.     "DrivingSkill": (3, 10000000),
  60.     "EngineeringSkill": (3, 10000000),
  61.     "DemolitionSkill": (3, 10000000),
  62.     "ThrowingSkill": (3, 10000000),
  63.     "MedicalSkill": (3, 10000000),
  64.     "MotorcycleSkill": (3, 10000000),
  65.     "StealthSkill": (3, 10000000),
  66.     "AviationSkill": (3, 10000000),
  67.     "ResistanceSkill": (3, 10000000),
  68. }
  69.  
  70. # Other constants
  71. USER = os.getlogin()
  72. DB_PATH = Path(f"C:/Users/{USER}/AppData/Local/SCUM/Saved/SaveFiles/SCUM.db")
  73.  
  74. BODY_SIM_KEY_PADDING = 5
  75. BODY_SIM_VALUE_PADDING = 10
  76.  
  77.  
  78. @dataclass
  79. class PropertyType:
  80.     """Just a small class to define property types as they occur in the body simulation blob."""
  81.  
  82.     name: bytes
  83.     width: int  # in bytes
  84.     # Used for converting with Python types
  85.     struct_type: Literal["<d", "<f", "<?"]
  86.  
  87.  
  88. DoubleProperty = PropertyType(name=b"DoubleProperty", width=8, struct_type="<d")
  89. FloatProperty = PropertyType(name=b"FloatProperty", width=4, struct_type="<f")
  90. BoolProperty = PropertyType(name=b"BoolProperty", width=1, struct_type="<?")
  91.  
  92.  
  93. def load_prisoner(con: sqlite3.Connection, id: int):
  94.     """Load prisoner from database."""
  95.     cur = con.execute("SELECT * FROM prisoner WHERE id = ?", (id,))
  96.     result = {desc[0]: val for desc, val in zip(cur.description, cur.fetchone())}
  97.     return result
  98.  
  99.  
  100. def save_prisoner(con: sqlite3.Connection, prisoner: dict):
  101.     """Updates prisoner in database. Currently only sets body_simulation."""
  102.     return con.execute(
  103.         "UPDATE prisoner SET body_simulation = ? WHERE id = ?",
  104.         (prisoner["body_simulation"], prisoner["id"]),
  105.     )
  106.  
  107.  
  108. def update_body_sim(body_sim: bytearray, key: bytes, value: float, property_type: PropertyType):
  109.     # Find the key in the body simulation blob
  110.     key_offset = body_sim.index(key)
  111.  
  112.     # Make sure we are using the correct property type
  113.     assert (
  114.         body_sim[
  115.             key_offset
  116.             + len(key)
  117.             + BODY_SIM_KEY_PADDING : key_offset
  118.             + len(key)
  119.             + BODY_SIM_KEY_PADDING
  120.             + len(property_type.name)
  121.         ]
  122.         == property_type.name
  123.     )
  124.  
  125.     # Calculate offset of actual value
  126.     value_offset = (
  127.         key_offset
  128.         + len(key)
  129.         + BODY_SIM_KEY_PADDING
  130.         + len(property_type.name)
  131.         + BODY_SIM_VALUE_PADDING
  132.     )
  133.  
  134.     # Convert value to bytes
  135.     value_bytes = struct.pack(property_type.struct_type, value)
  136.  
  137.     # Update value in body sim blob
  138.     body_sim[value_offset : value_offset + property_type.width] = value_bytes
  139.  
  140.  
  141. def update_skills(con: sqlite3.Connection, prisoner: dict):
  142.     """Sets all skills to max level in the database."""
  143.  
  144.     for (name,) in con.execute(
  145.         "SELECT name FROM prisoner_skill WHERE prisoner_id = ?", (prisoner["id"],)
  146.     ):
  147.         if name not in SET_SKILLS:
  148.             continue
  149.  
  150.         new_level, new_experience = SET_SKILLS[name]
  151.  
  152.         # Finally, update the XML and other fields in the database
  153.         con.execute(
  154.             "UPDATE prisoner_skill SET level = ?, experience = ? WHERE prisoner_id = ? AND name = ?",
  155.             (new_level, new_experience, prisoner["id"], name),
  156.         )
  157.  
  158.  
  159. def choose_prisoner(con: sqlite3.Connection):
  160.     """Choose prisoner to update."""
  161.     cur = con.execute(
  162.         "SELECT prisoner.id, user_profile.name FROM prisoner LEFT JOIN user_profile ON prisoner.user_profile_id = user_profile.id WHERE user_profile.authority_name is ?",
  163.         (None,),
  164.     )
  165.     print("\nFound prisoners in local single player:\n")
  166.     for id, name in cur:
  167.         print(f'"{name}" with ID {id}')
  168.     return int(input("\nEnter prisoner ID: "))
  169.  
  170.  
  171. def main():
  172.     print("Backing up database... ")
  173.     filename_safe_iso = dt.datetime.now().isoformat().replace(":", "-")
  174.     backup_path = DB_PATH.with_name(f"SCUM-bak-{filename_safe_iso}.db")
  175.     shutil.copy(DB_PATH, backup_path)
  176.     print(f"Backed up to: {backup_path}")
  177.  
  178.     print("\nConnecting to database...")
  179.     con = sqlite3.connect(DB_PATH)
  180.  
  181.     # Choose prisoner interactively
  182.     # prisoner_id = choose_prisoner(con)
  183.     prisoner_id = PRISONER_ID
  184.  
  185.     print(f"Loading prisoner with ID {prisoner_id}...")
  186.     prisoner = load_prisoner(con, prisoner_id)
  187.  
  188.     print("\nUpdating attributes... ", end="")
  189.     body_sim = bytearray(prisoner["body_simulation"])
  190.  
  191.     for attribute, value in SET_ATTRIBUTES.items():
  192.         update_body_sim(
  193.             body_sim,
  194.             attribute.encode("ascii"),
  195.             value,
  196.             DoubleProperty,
  197.         )
  198.  
  199.     prisoner["body_simulation"] = bytes(body_sim)
  200.  
  201.     save_prisoner(con, prisoner)
  202.     print("Success!")
  203.  
  204.     print("Updating skills... ", end="")
  205.     update_skills(con, prisoner)
  206.     print("Success!")
  207.  
  208.     con.commit()
  209.     input("\nAll done! Press enter to exit.")
  210.  
  211.  
  212. if __name__ == "__main__":
  213.     try:
  214.         main()
  215.     except KeyboardInterrupt:
  216.         print("\nExiting...")
  217.     except Exception:
  218.         print("\n\nSomething went wrong...\n\n")
  219.         traceback.print_exc()
  220.         input("\n\nPress enter to exit.")
  221.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement