From 3adb7c553a8c8c2bad21940e9d3c8f35ea6ac94a Mon Sep 17 00:00:00 2001 From: "B. Stack" Date: Mon, 11 Mar 2024 17:13:40 -0400 Subject: get/set purchased weapons --- srb_lib.py | 193 +++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 143 insertions(+), 50 deletions(-) (limited to 'srb_lib.py') diff --git a/srb_lib.py b/srb_lib.py index 1b13175..32a4f2e 100644 --- a/srb_lib.py +++ b/srb_lib.py @@ -20,7 +20,7 @@ # Dependencies: import sys, struct -srb_lib_version = "20240310a" +srb_lib_version = "20240311a" # 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 "Z15 - return -1, f"invalid way to reference weapon: [{type(weapon)}]. Use index or name." + return {}, f"invalid index {weapon}; use 0-15" # must be <0 or >15 + return {}, f"invalid way to reference weapon: [{type(weapon)}]. Use index or name." + +def get_purchased_weapons(data_object,profile_id,silent=False): + """ + For the given profile, return which weapons are already purchased, as a comma-separated string and a bitmask. + """ + data = _get_data_from_data_object(data_object) + weapons_purchased = struct.unpack_from('<1i',data,PROFILE_START_POSITION[profile_id]+POS_BYTES_WEAPONS_PURCHASED)[0] + #print(f"debug: got weapons_purchased 0x{weapons_purchased:0x}") + all_weapons_mask = [i for i in WEAPONS if i["name"] == "all"][0]["p"] + none_weapons_mask = [i for i in WEAPONS if i["name"] == "none"][0]["p"] + # short-circuit if all + if weapons_purchased & all_weapons_mask == all_weapons_mask: + return "all", all_weapons_mask + # short-circuit if none + elif weapons_purchased | none_weapons_mask == 0: + return "none", none_weapons_mask + weapons_list = [] + weapons_mask = 0x0 + for i in WEAPONS: + #result = weapons_purchased & i["p"] + #print(f"debug: checking {i['p']:x} \"{i['name']}\" bitwise against {weapons_purchased:x}: {result:x}") + if weapons_purchased & i["p"] and (i["name"] not in ["all","none"]): + weapons_list.append(i["name"]) + weapons_mask += i["p"] + if not silent: + print(f"debug: currently have 0x{weapons_mask:04x} b{weapons_mask:016b}, {weapons_list}") + return ','.join(weapons_list), weapons_mask def get_level_status(data_object,profile_id,level): """ WORKHERE: not sure how to use this, but making sure my position in the data works. """ @@ -310,7 +353,6 @@ def get_name(data_object,profile_id): def set_name(data_object,profile_id,new_name): """ Set the name of the profile_id. Note that this allows you to set a name for a profile that is not in use, which preseeds the name when you select to make a profile in this slot in-game. """ data = _get_data_from_data_object(data_object) - data_bytearray = bytearray(data) # convert new_name to tuple of integer values name_array = [] for i in new_name.upper(): @@ -325,11 +367,13 @@ def set_name(data_object,profile_id,new_name): if len(name_array) > 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) + # How I was doing this before: + #data_bytearray = bytearray(data) + #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) + data = srb_pack('<8i',data,PROFILE_START_POSITION[profile_id]+POS_NAME+(4*x),*name_array) #print(f"debug: after setting name to {new_name}, we checked and got {get_name(data,profile_id)}") return data, "" @@ -341,6 +385,53 @@ def get_profile_in_use(data_object,profile_id): #print(f"debug: in_use: {in_use}, type {type(in_use)}") return in_use +def get_tutorial_completed(data_object,profile_id): + """ Print if the profile has completed the tutorial. """ + data = _get_data_from_data_object(data_object) + # it always comes as a tuple + tutorial_completed = struct.unpack_from('<1?',data,PROFILE_START_POSITION[profile_id]+POS_TUTORIAL_COMPLETED)[0] + return tutorial_completed + +def set_tutorial_completed(data_object,profile_id,completed): + """ Set tutorial-completed for given profile. """ + data = _get_data_from_data_object(data_object) + data = srb_pack('