aboutsummaryrefslogtreecommitdiff
path: root/srb_lib.py
diff options
context:
space:
mode:
authorB. Stack <bgstack15@gmail.com>2024-03-20 09:42:35 -0400
committerB. Stack <bgstack15@gmail.com>2024-03-20 09:42:35 -0400
commit30b5e3c1c1fd9de17ed57f8b5a51699be1e1e784 (patch)
tree406f66362592174645ee2e9f86d24f8f7445048e /srb_lib.py
parentminor refactoring, clean up extra comments (diff)
downloadsrb_lib-30b5e3c1c1fd9de17ed57f8b5a51699be1e1e784.tar.gz
srb_lib-30b5e3c1c1fd9de17ed57f8b5a51699be1e1e784.tar.bz2
srb_lib-30b5e3c1c1fd9de17ed57f8b5a51699be1e1e784.zip
simplify checksum, rearrange position bytes
* simplify checksum work by packing its value as little-endian unsigned. * rearranged bytes to be in order
Diffstat (limited to 'srb_lib.py')
-rw-r--r--srb_lib.py70
1 files changed, 27 insertions, 43 deletions
diff --git a/srb_lib.py b/srb_lib.py
index 3daae7b..874c287 100644
--- a/srb_lib.py
+++ b/srb_lib.py
@@ -25,24 +25,36 @@
# Dependencies:
import sys, struct
-srb_lib_version = "20240320a"
+srb_lib_version = "20240320b"
# 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.
# "Z<dddddddd" is the start of a profile.
-CHECKSUM_LENGTH = 0x4
+INT_SIZE = 0x4 # unsigned; no negative numbers used in this game or data structure
+CHECKSUM_LENGTH = INT_SIZE
PROFILE_START_POSITION = [0, 0x10, 0x142C, 0x2848]
+
+# There are additional values stored, including Options but you can change those in the menu so are not implemented in this tool.
+POS_BYTES_WEAPONS_PURCHASED = 0x028C
+POS_LEVEL_BALLOONS_MULTIPLIER_LEVELSET = 0x238
POS_MONEY = 0x270
POS_HEALTH = 0x274 # 0-3 is available levels
-POS_STUNT = 0x280 # 0-3 is available levels
POS_GUN = 0x27C # 0-3 is available levels, although 4 is octo-gun
+POS_STUNT = 0x280 # 0-3 is available levels
POS_EQUIPPED_WEAPON = 0x284
+POS_PLANES_PURCHASED = 0x288
POS_PROFILE_IN_USE = 0x290
POS_NAME = 0x294
+POS_LEVELSET_COMPLETED_MISSIONS_MASK = 0x2B8
+POS_TUTORIAL_COMPLETED = 0x2D0 # bool
+POS_LEVEL_START = 0x2D4
+POS_LEVELSET_COMPLETED_LETTERS_COUNT = 0x3A0
+POS_LEVEL_LETTERS = 0x3E8
+POS_LEVEL_LETTERS_MULTIPLIER_LEVELSET = 7
+POS_LEVEL_BALLOONS = 0x4B4
+POS_LEVEL_BALLOONS_MULTIPLIER_LEVEL = 0x50
-NAME_CHARS = " ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!?"
+NAME_CHARS = " ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!?" # in order
-POS_BYTES_WEAPONS_PURCHASED = 0x028C
-# Woodstock Missile and Bottle Rockets are always available to use.
# for equipped-weapon
# id = value of equipped byte
# e = equippable
@@ -66,7 +78,6 @@ WEAPONS = [
{"id":0xF,"e":False,"p":0x6F60,"name":"all"}, # custom for this library
]
-POS_PLANES_PURCHASED = 0x288
PLANES = [
{"p":0x0000,"name":"none"},
{"p":0x0100,"name":"marcie"},
@@ -91,17 +102,6 @@ LEVELSETS = [
{"id":5,"l":2,"c":"none" ,"b":0,"le":"","name":"Flying Fortress"},
]
-INT_SIZE = 0x4
-# relative to player profile
-POS_LEVEL_START = 0x2D4
-POS_LEVEL_BALLOONS = 0x4B4
-POS_LEVEL_BALLOONS_MULTIPLIER_LEVELSET = 0x238
-POS_LEVEL_BALLOONS_MULTIPLIER_LEVEL = 0x50
-POS_LEVELSET_COMPLETED_LETTERS_COUNT = 0x3A0
-POS_LEVEL_LETTERS = 0x3E8
-POS_LEVEL_LETTERS_MULTIPLIER_LEVELSET = 7
-POS_LEVELSET_COMPLETED_MISSIONS_MASK = 0x2B8
-
# pos_r = relative position for level status
# set_pos = level number within the levelset. Necessary for certain position calculations including letters.
LEVELS = [
@@ -156,8 +156,6 @@ LEVEL_STATUSES = [
{"b":0x64,"name":"general"}, # depends on all secondary objectives, all letters, all balloons, under time, at least 50/100 health, not lost any lives.
]
-POS_TUTORIAL_COMPLETED = 0x2D0 # bool
-
# DEFINE FUNCTIONS
def ferror(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
@@ -177,22 +175,15 @@ def read_file(filename):
with open(filename, 'rb') as f:
# Read the entire file
file_contents = f.read()
- checksum = file_contents[0:4]
- file_contents_without_checksum = file_contents[4:]
+ checksum = struct.unpack_from('<1I',file_contents,0)[0]
+ file_contents_without_checksum = file_contents[CHECKSUM_LENGTH:]
return checksum, file_contents_without_checksum
def write_file(filename, checksum, data):
""" Write the checksum and data to the file. We are assuming the checksum is already correct. """
- print(f"debug: checksum(type={type(checksum).__name__})={checksum}")
- if type(checksum) == int:
- checksum = checksum
- elif type(checksum) == str:
- checksum = int(checksum)
- elif type(checksum) == bytes:
- checksum = int.from_bytes(checksum)
+ #print(f"debug: checksum(type={type(checksum).__name__})={checksum}")
with open(filename, 'wb') as f:
- f.write(struct.pack('>L',checksum))
- #f.write(struct.pack('>I',int(checksum)))
+ f.write(struct.pack('<L',checksum))
f.write(data)
######## start embedded file
@@ -821,16 +812,9 @@ def calculate_checksum(data):
# The polynomial appears to be a standard one like used for Ethernet, but I am uncertain of the xor is standard or not.
# I learned all this only with <https://github.com/8051Enthusiast/delsum>
# crc width=32 poly=0x4c11db7 init=0x0 xorout=0x235b4b9c refin=false refout=false out_endian=little
- csum = hex(crc_poly(data, 32, 0x4C11DB7, crc=0x0, ref_in=False, ref_out=False, xor_out=0x235b4b9c))
- # comes in as ceb434d4, but needs to be d434b4ce, and bytearray is convenient way to swap it like that
- # trim off the '0x' from the string
- print(f"debug: Got csum={csum}")
- # pad any odd-digit-count hex value with a leading 0 for bytearray.fromhex() which is not very smart.
- csum = ("0" if len(str(csum)) % 2 else "") + str(csum)[2:]
- csum = bytearray.fromhex(csum)
- csum.reverse()
- # and back to bytes because that is how we will want to use it.
- return bytes(csum)
+ csum = crc_poly(data, 32, 0x4C11DB7, crc=0x0, ref_in=False, ref_out=False, xor_out=0x235b4b9c)
+ # Note that this value will be stored little-endian in the file. That detail does not matter here.
+ return csum
def correct_file(filename, debuglevel = 0):
""" Given a savegame file, calculate the correct checksum and store that sum instead of whatever is there. """
@@ -839,10 +823,10 @@ def correct_file(filename, debuglevel = 0):
ran = False
if csum != cs:
if debuglev(5,debuglevel):
- ferror(f"Stored checksum is {cs} and will update it to {csum}")
+ ferror(f"Stored checksum is 0x{cs:04x} and will update it to 0x{csum:04x}")
write_file(filename,csum,data)
ran = True
else:
if debuglev(2,debuglevel):
- ferror(f"Stored checksum is still correct, {csum}. Skipping {filename}")
+ ferror(f"Stored checksum is still correct, 0x{csum:04x}. Skipping {filename}")
return ran
bgstack15