From 1d0a6c692218703677cdcf8e6ceb98640ad36515 Mon Sep 17 00:00:00 2001 From: "B. Stack" Date: Sun, 10 Mar 2024 23:05:43 -0400 Subject: add get/set name --- srb.py | 18 ++++++++++++++++-- srb_lib.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/srb.py b/srb.py index 800d1c5..9c6a941 100755 --- a/srb.py +++ b/srb.py @@ -27,6 +27,7 @@ parser = argparse.ArgumentParser(description="Cli tool for manipulating savegame formatter_class=argparse.RawDescriptionHelpFormatter, epilog=f"""LEVELS include {[i['name'] for i in srb_lib.LEVELS]+list(range(0,len(srb_lib.LEVELS)))} WEAPONS include {[i for i in srb_lib.WEAPONS if i != "undefined"]+list(range(0,16))} +NAME_CHARS include "{srb_lib.NAME_CHARS}" """) parser.add_argument("-V","--version",action="version",version="%(prog)s " + srb_lib.srb_lib_version) parser.add_argument("-d","--debug",nargs='?',default=0,type=int,choices=range(0,11),help="Set debug level") @@ -37,8 +38,11 @@ parser.add_argument("--get-weapon",action="store_true",help="Print currently equ # choices seems to be too strict here for the numbers. We can live with just the #choices=[i for i in srb_lib.WEAPONS if i != "undefined"]+list(range(0,16)) #parser.add_argument("--set-weapon",choices=[i for i in srb_lib.WEAPONS if i != "undefined"],help="Print currently equipped weapon for profile.") -parser.add_argument("--set-weapon",help="Print currently equipped weapon for profile.") +parser.add_argument("--set-weapon",help="Set currently equipped weapon for profile.") parser.add_argument("--get-level",help="Print status for this level for profile.") +parser.add_argument("--get-name",action="store_true",help="Print name for profile.") +parser.add_argument("--set-name",help="Set name for profile.") +parser.add_argument("--get-profile-in-use",action="store_true",help="Print if profile is in use.") parser.add_argument("--checksum",action=argparse.BooleanOptionalAction,default=True,help="Correct checksum. Default is to do this. It happens at the end of everything else.") parser.add_argument("file",default="Profile 1.sav") args = parser.parse_args() @@ -57,7 +61,7 @@ profile_id = args.profile #print(f"profile_id={profile_id}") # WORKHERE: new actions that need --profile must be added here. -if not profile_id and (args.get_money or args.set_money or args.get_weapon or args.set_weapon or args.get_level): +if not profile_id and (args.get_money or args.set_money or args.get_weapon or args.set_weapon or args.get_level or args.get_name or args.set_name or args.get_profile_in_use): ferror("Warning: Cannot perform most actions without --profile. Not all tasks may run.") else: if args.get_money: @@ -84,6 +88,16 @@ else: srb_lib.write_file(args.file,0,data) if args.get_level: print(f"Profile {profile_id} has level {args.get_level} status {srb_lib.get_level_status(args.file,profile_id,args.get_level)}") + if args.get_name: + print(f"Profile {profile_id} has name {srb_lib.get_name(args.file,profile_id)}") + if args.set_name: + data, message = srb_lib.set_name(args.file, profile_id, args.set_name) + if (type(data) == int and data == -1) or message != "": + ferror(f"Failed to set profile {profile_id} name to {args.set_name} because {message}") + else: + srb_lib.write_file(args.file,0,data) + if args.get_profile_in_use: + print(f"Profile {profile_id} in use is {srb_lib.get_profile_in_use(args.file,profile_id)}") if args.checksum: f = args.file diff --git a/srb_lib.py b/srb_lib.py index dc69614..1b13175 100644 --- a/srb_lib.py +++ b/srb_lib.py @@ -18,16 +18,19 @@ # winetricks vd=1024x768 # winetricks vd=off # Dependencies: -# python3-crcmod import sys, struct -srb_lib_version = "20240309a" +srb_lib_version = "20240310a" # Table of byte positions of values in the savegame file, minus the first four bytes which are the checksum. Due to zero-indexing of python lists, but for ease of usage, we will always put a zero as the value of index 0. That is, profile 1 will use index 1 of the list. # money is 0x270 bytes after the "Z 8 or len(name_array) < 0: + return -1, f"Invalid length {len(name_array)} of name {new_name}." + x = 0 + # for some reason I cannot figure out '<8i' with a tuple or list of ints. So just do it manually. + for i in name_array: + struct.pack_into('<1i',data_bytearray,PROFILE_START_POSITION[profile_id]+POS_NAME+(4*x),i) + x = x + 1 + data = bytes(data_bytearray) + #print(f"debug: after setting name to {new_name}, we checked and got {get_name(data,profile_id)}") + return data, "" + +def get_profile_in_use(data_object,profile_id): + """ Print if the profile_id is in use. """ + data = _get_data_from_data_object(data_object) + # it always comes as a tuple + in_use = struct.unpack_from('<1?',data,PROFILE_START_POSITION[profile_id]+POS_PROFILE_IN_USE)[0] + #print(f"debug: in_use: {in_use}, type {type(in_use)}") + return in_use + def calculate_checksum(data): """ Return the 4-byte checksum used by the game for the provided data. """ # aka # CRC-32/BZIP2 -- cgit