chore: initialize age-reimpl repo
Reverse-engineering + open reimplementation workspace for Eushully's AGE/SYS4 engine (first target: Himegari). The repo root is age-reimpl/; the original game install and the extracted ALF data are siblings outside the repo and are never tracked. build/ (derived corpora) is gitignored and regenerated by the tools. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
101
tools/probe_types.py
Normal file
101
tools/probe_types.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""Corpus-wide: verify T1/T2/T3 target-dword types, test table completeness,
|
||||
and do a stricter string scan.
|
||||
"""
|
||||
import struct
|
||||
from pathlib import Path
|
||||
from collections import Counter
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2] / "extracted" / "DATA1"
|
||||
|
||||
def parse(path):
|
||||
data = path.read_bytes()
|
||||
f = struct.unpack_from("<13I", data, 8)
|
||||
body = data[0x3C:]
|
||||
n = len(body) // 4
|
||||
dws = struct.unpack_from(f"<{n}I", body, 0)
|
||||
return f, dws, body
|
||||
|
||||
def main():
|
||||
files = sorted(ROOT.glob("*.BIN"))
|
||||
type_hist = {1: Counter(), 2: Counter(), 3: Counter()}
|
||||
complete = {1: Counter(), 2: Counter(), 3: Counter()}
|
||||
for p in files:
|
||||
f, dws, body = parse(p)
|
||||
code_end = f[8]
|
||||
specs = {1: (f[7], f[8], 0x71), 2: (f[9], f[10], 0x03), 3: (f[11], f[12], 0x8F)}
|
||||
for t, (cnt, off, expect) in specs.items():
|
||||
targets = dws[off:off + cnt]
|
||||
for e in targets:
|
||||
type_hist[t][dws[e] if e < len(dws) else "OOB"] += 1
|
||||
# naive count of (expect, 0, X) triples in code, at any alignment
|
||||
naive = sum(1 for i in range(code_end - 2)
|
||||
if dws[i] == expect and dws[i + 1] == 0)
|
||||
if cnt == naive:
|
||||
complete[t]["exact"] += 1
|
||||
elif cnt < naive:
|
||||
complete[t]["table<naive"] += 1
|
||||
else:
|
||||
complete[t]["table>naive"] += 1
|
||||
for t in (1, 2, 3):
|
||||
print(f"T{t} target-dword histogram: "
|
||||
+ ", ".join(f"{v:#x}x{n}" for v, n in type_hist[t].most_common(6)))
|
||||
print(f"T{t} completeness vs naive scan: {dict(complete[t])}")
|
||||
|
||||
# value ranges of the operands referenced by each table (sample corpus-wide)
|
||||
for t, expect in ((1, 0x71), (2, 0x03), (3, 0x8F)):
|
||||
lo, hi = None, None
|
||||
vals = Counter()
|
||||
for p in files:
|
||||
f, dws, body = parse(p)
|
||||
cnt, off = (f[7], f[8]) if t == 1 else (f[9], f[10]) if t == 2 else (f[11], f[12])
|
||||
for e in dws[off:off + cnt]:
|
||||
if e + 2 < len(dws):
|
||||
v = dws[e + 2]
|
||||
vals[v] += 1
|
||||
lo = v if lo is None else min(lo, v)
|
||||
hi = v if hi is None else max(hi, v)
|
||||
print(f"T{t} operand values: min={lo and hex(lo)}, max={hi and hex(hi)}, "
|
||||
f"top: {', '.join(f'{v:#x}x{n}' for v, n in vals.most_common(8))}")
|
||||
|
||||
def good_string(bs):
|
||||
"""bs = decoded-candidate raw bytes (already XOR'd). Strict cp932 validity."""
|
||||
i, chars = 0, 0
|
||||
while i < len(bs):
|
||||
b = bs[i]
|
||||
if 0x20 <= b <= 0x7E:
|
||||
i += 1; chars += 1
|
||||
elif 0x81 <= b <= 0x9F or 0xE0 <= b <= 0xEA:
|
||||
if i + 1 < len(bs) and (0x40 <= bs[i+1] <= 0xFC) and bs[i+1] != 0x7F:
|
||||
i += 2; chars += 1
|
||||
else:
|
||||
return 0
|
||||
else:
|
||||
return 0
|
||||
return chars
|
||||
|
||||
def scan(name, limit=25, min_chars=3):
|
||||
f, dws, body = parse(ROOT / name)
|
||||
n = len(body) // 4
|
||||
print(f"\n=== strings in {name} (strict) ===")
|
||||
found, i = 0, 0
|
||||
while i < n and found < limit:
|
||||
raw = bytearray()
|
||||
j = i
|
||||
while j < n:
|
||||
chunk = bytes(b ^ 0xFF for b in body[j*4:(j+1)*4])
|
||||
raw += chunk
|
||||
j += 1
|
||||
if 0 in chunk:
|
||||
break
|
||||
s = bytes(raw).split(b"\0")[0]
|
||||
if len(s) >= 2 and good_string(s) >= min_chars:
|
||||
print(f" [{i:#x}] {s.decode('cp932')!r}")
|
||||
found += 1
|
||||
i = j
|
||||
else:
|
||||
i += 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
scan("SC0030.BIN")
|
||||
scan("MENU.BIN", limit=12)
|
||||
Reference in New Issue
Block a user