Skip to content

Instantly share code, notes, and snippets.

@Jan200101
Created December 4, 2023 11:56
Show Gist options
  • Save Jan200101/dbe2435a18971c006f207f1dbf383c26 to your computer and use it in GitHub Desktop.
Save Jan200101/dbe2435a18971c006f207f1dbf383c26 to your computer and use it in GitHub Desktop.
Calculate Structs and relevant padding for Northstar offset structs
import sys
if len(sys.argv) != 2:
print("calc.py [input]")
exit(1)
with open(sys.argv[1], "r") as fd:
data = fd.read()
MAX_SIZE = None
FIELDS = {}
def get_type_size(typeinfo: str) -> int:
if typeinfo[-1] == "*":
return 8
typeinfo = {
"ePersistenceReady": "char",
"eSignonState": "int"
}.get(typeinfo, typeinfo)
if typeinfo == "char":
return 1
if typeinfo == "bool":
return 1
if typeinfo == "int":
return 4
if typeinfo == "uint32_t":
return 4
if typeinfo == "int32_t":
return 4
if typeinfo == "float":
return 4
if typeinfo == "double":
return 8
if typeinfo == "Vector3":
return 4*3
raise Exception(f"Unknown size for {typeinfo}")
def get_array_multiplier(array_info: str) -> int:
multiplier = 0
try:
last_array_start = 0
while True:
array_start = array_info.index("[", last_array_start)+1
array_end = array_info.index("]", array_start)
size = array_info[array_start:array_end]
size = {
"PERSISTENCE_MAX_SIZE": "0xDDCD"
}.get(size, size)
base = 10
if "0x" in size:
base = 16
size = int(size, base)
if multiplier:
multiplier *= size
else:
multiplier = size
last_array_start = array_start
except ValueError:
pass
if not multiplier:
multiplier = 1
return multiplier
for line in data.split("\n"):
line = line.strip()
if not line:
continue
if line.startswith("STRUCT_SIZE"):
if MAX_SIZE is not None:
raise Exception("struct size specified multiple times")
num_start = line.index("(")+1
num_end = line.index(")")
numstr = line[num_start:num_end]
base = 10
if "0x" in numstr:
base = 16
MAX_SIZE = int(numstr, base)
print(f"Set Max Size to {hex(MAX_SIZE).upper()}")
elif line.startswith("FIELD"):
offset_start = line.index("(")+1
offset_end = line.index(",")
offsetstr = line[offset_start:offset_end]
base = 10
if "0x" in offsetstr:
base = 16
offset = int(offsetstr, base)
#print(offset)
signature_end = line.index(")")
signature = line[offset_end+1:signature_end].strip()
typeinfo_end = signature.index("m_")
typeinfo = signature[:typeinfo_end].strip()
typesize = get_type_size(typeinfo)
var_end = signature.find("[")
if var_end < 0:
var_end = len(signature)
varname = signature[typeinfo_end:var_end]
array_info = signature[var_end:]
array_multiplier = get_array_multiplier(array_info)
typesize *= array_multiplier
if varname in FIELDS:
raise Exception(f"Duplicate name {varname}")
FIELDS[varname] = {
"signature": signature,
"offset": offset,
"size": typesize
}
else:
raise Exception("Unknown instruction")
if MAX_SIZE is not None:
MEMORY = [None] * MAX_SIZE
else:
MEMORY = []
print("Finding empty space")
for name, info in FIELDS.items():
offset = info["offset"]
size = info["size"]
for x in range(size):
x_offset = offset+x
if MAX_SIZE is not None and x_offset > MAX_SIZE:
raise Exception("Out of bounds access")
try:
if MEMORY[x_offset]:
raise Exception(f"Found memory set at {x_offset} by {MEMORY[x_offset]} while checking {name}")
except IndexError:
pass
while True:
try:
MEMORY[x_offset] = name
break
except IndexError:
increase_to = offset+(size-1)
print(f"Increasing memory to {increase_to} for {name}")
while len(MEMORY) <= increase_to:
MEMORY.append(None)
print("Creating Padding")
padding_index = 0
padding_start = None
for i, v, in enumerate(MEMORY):
if v is None and i != len(MEMORY)-1:
if padding_start is None:
padding_start = i
else:
if i == len(MEMORY) - 1:
i += 1
if padding_start is not None:
padding_index += 1
padding_name = f"_unk{padding_index}"
padding_size = i - padding_start
padding_offset = padding_start
set_memory = [x for x in MEMORY[padding_offset:padding_offset+padding_size] if x is not None]
if set_memory:
raise Exception(f"Invalid padding {padding_start} {i} found {set_memory}")
padding_start = None
FIELDS[padding_name] = {
"signature": f"char {padding_name}[{padding_size}]",
"offset": padding_offset,
"size": padding_size
}
MAX_SIZE = len(MEMORY)
MEMORY = [None] * MAX_SIZE
print("Validating no empty space is left")
for name, info in FIELDS.items():
offset = info["offset"]
size = info["size"]
for x in range(size):
x_offset = offset+x
if MAX_SIZE is not None and x_offset > MAX_SIZE:
raise Exception("Out of bounds access")
if MEMORY[x_offset]:
raise Exception(f"Found memory set at {x_offset} by {MEMORY[x_offset]} while checking {name}")
MEMORY[x_offset] = name
empty_space = [i for i, x in enumerate(MEMORY) if x is None]
if empty_space:
raise Exception(f"Found empty space: {min(empty_space)}, {max(empty_space)}")
print("Sorting results")
SORTED_FIELDS = {k: v for k, v in sorted(FIELDS.items(), key=lambda item: item[1]["offset"])}
with open(sys.argv[1]+".out", "w") as fd:
for name, field in SORTED_FIELDS.items():
signature = field["signature"]
offset = field["offset"]
size = field["size"]
fd.write(f"{signature}; // {hex(offset)} ( Size: {size} )\n")
#print(SORTED_FIELDS)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment