Created
December 4, 2023 11:56
-
-
Save Jan200101/dbe2435a18971c006f207f1dbf383c26 to your computer and use it in GitHub Desktop.
Calculate Structs and relevant padding for Northstar offset structs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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