Crusader_Decomp/tmp_immortality_scan.py

113 lines
4.4 KiB
Python
Raw Normal View History

import csv
import pathlib
import struct
ROOT = pathlib.Path(r"K:/ghidra/Crusader_Decomp")
TARGETS = {189, 190, 191, 272, 273, 283, 285}
TARGET_COMPARE_CLASSES = {"NPCTRIG", "COR_BOOT", "REE_BOOT", "SFXTRIG"}
def find_all(haystack: bytes, needle: bytes) -> list[int]:
offsets: list[int] = []
start = 0
while True:
found = haystack.find(needle, start)
if found < 0:
return offsets
offsets.append(found)
start = found + 1
def lcp(left: bytes, right: bytes) -> int:
count = 0
limit = min(len(left), len(right))
while count < limit and left[count] == right[count]:
count += 1
return count
def lcs(left: bytes, right: bytes) -> int:
count = 0
limit = min(len(left), len(right))
while count < limit and left[-1 - count] == right[-1 - count]:
count += 1
return count
rows = list(
csv.DictReader(
(ROOT / "USECODE/EUSECODE_extracted/class_event_index.tsv").open("r", encoding="utf-8"),
delimiter="\t",
)
)
rows_by_entry: dict[int, list[dict[str, object]]] = {}
for row in rows:
entry_index = int(row["entry_index"])
if entry_index not in TARGETS:
continue
if not row["derived_body_start"]:
continue
rows_by_entry.setdefault(entry_index, []).append(
{
"class_name": row["class_name_hint"],
"slot": int(row["slot"], 0),
"event_name_hint": row["event_name_hint"],
"body_start": int(row["derived_body_start"], 0),
"body_end": int(row["derived_body_end"], 0),
}
)
chunk_files: dict[int, pathlib.Path] = {}
for chunk_path in (ROOT / "USECODE/EUSECODE_extracted/chunks").glob("chunk_*.bin"):
entry_index = int(chunk_path.name.split("_")[1])
if entry_index in rows_by_entry:
chunk_files[entry_index] = chunk_path
bodies: dict[tuple[str, int], bytes] = {}
for entry_index in sorted(rows_by_entry):
chunk_path = chunk_files[entry_index]
data = chunk_path.read_bytes()
class_name = str(rows_by_entry[entry_index][0]["class_name"])
print(f"ENTRY {entry_index} {class_name} FILE {chunk_path.name}")
for row in sorted(rows_by_entry[entry_index], key=lambda item: int(item["body_start"])):
body = data[int(row["body_start"]):int(row["body_end"])]
class_name = str(row["class_name"])
slot = int(row["slot"])
bodies[(class_name, slot)] = body
hits_0410_16 = find_all(body, struct.pack("<H", 0x0410))
hits_0410_32 = find_all(body, struct.pack("<I", 0x00000410))
hits_1004_16 = find_all(body, struct.pack("<H", 0x1004))
print(
"BODY class={class_name} slot=0x{slot:02X} hint={hint} start=0x{start:04X} end=0x{end:04X} len={length} le16_0410={count16}:{offs16} le32_00000410={count32}:{offs32} le16_1004={count1004}:{offs1004} first16={first16} last16={last16}".format(
class_name=class_name,
slot=slot,
hint=str(row["event_name_hint"] or "-"),
start=int(row["body_start"]),
end=int(row["body_end"]),
length=len(body),
count16=len(hits_0410_16),
offs16=",".join(f"0x{offset:04X}" for offset in hits_0410_16[:16]) or "-",
count32=len(hits_0410_32),
offs32=",".join(f"0x{offset:04X}" for offset in hits_0410_32[:16]) or "-",
count1004=len(hits_1004_16),
offs1004=",".join(f"0x{offset:04X}" for offset in hits_1004_16[:16]) or "-",
first16=body[:16].hex(),
last16=body[-16:].hex(),
)
)
print()
print("TOP_STRUCTURAL_PAIRS")
comparisons: list[tuple[int, int, int, tuple[str, int], tuple[str, int], int, int]] = []
compare_keys = [key for key in bodies if key[0] in TARGET_COMPARE_CLASSES]
for left_index, left_key in enumerate(compare_keys):
for right_key in compare_keys[left_index + 1:]:
left_body = bodies[left_key]
right_body = bodies[right_key]
prefix = lcp(left_body, right_body)
suffix = lcs(left_body, right_body)
comparisons.append((prefix + suffix, prefix, suffix, left_key, right_key, len(left_body), len(right_body)))
comparisons.sort(reverse=True)
for total, prefix, suffix, left_key, right_key, left_len, right_len in comparisons[:12]:
print(
f"PAIR {left_key[0]}:0x{left_key[1]:02X} len={left_len} <-> {right_key[0]}:0x{right_key[1]:02X} len={right_len} prefix={prefix} suffix={suffix} total={total}"
)