mirror of
https://github.com/kuraeow/ultratears.git
synced 2026-11-07 10:16:00 -10:00
70 lines
3.1 KiB
Python
70 lines
3.1 KiB
Python
"""Lift the three TOTK code modules to C (tools/lifter/gen.py), in parallel.
|
|
|
|
usage: python tools/gen_game.py <exefs dir> <output dir>
|
|
Bases must match src/runtime/rt.h (MAIN_BASE, SUBSDK0_BASE, SDK_BASE).
|
|
"""
|
|
import pathlib
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
|
MODULES = {'main': 0x0800000000, 'subsdk0': 0x0804000000, 'sdk': 0x0805000000}
|
|
# Windows exit codes of a lifter that crashed (issue #30: all three ended with 3221226505 and an empty summary)
|
|
CRASHES = {0xC0000005: 'access violation in native code (unicorn / capstone / lz4 or Python itself)',
|
|
0xC0000409: 'fast fail: Python or a native module aborted (a fatal error, a failed allocation, a stack check)',
|
|
0xC00000FD: 'stack overflow', 0xC0000017: 'out of memory', 0xC000001D: 'illegal instruction (the CPU lacks a feature)'}
|
|
|
|
|
|
def main():
|
|
exefs, out = pathlib.Path(sys.argv[1]), pathlib.Path(sys.argv[2])
|
|
t0 = time.time()
|
|
procs = []
|
|
for name, base in MODULES.items():
|
|
(out / name).mkdir(parents=True, exist_ok=True)
|
|
log = open(out / f'{name}.log', 'w')
|
|
procs.append((name, log, subprocess.Popen(
|
|
[sys.executable, '-X', 'faulthandler', str(ROOT / 'tools/lifter/gen.py'), name, str(exefs / name), hex(base), str(out / name),
|
|
'--simd', 'c'], stdout=log, stderr=subprocess.STDOUT)))
|
|
# one progress line: the lifters' "progress <functions done>/<functions>" lines (none while they analyse)
|
|
sys.path.insert(0, str(ROOT / 'tools'))
|
|
import progress
|
|
bar = progress.Bar('translating the code', 0, 'functions')
|
|
while any(p.poll() is None for _, _, p in procs):
|
|
done = total = known = 0
|
|
for name, _, p in procs:
|
|
try:
|
|
tail = (out / f'{name}.log').read_bytes()[-400:].decode(errors='replace')
|
|
except OSError:
|
|
continue
|
|
m = re.findall(r'progress (\d+)/(\d+)', tail)
|
|
if m:
|
|
done, total, known = done + int(m[-1][0]), total + int(m[-1][1]), known + 1
|
|
elif p.poll() is not None:
|
|
known += 1
|
|
# a percentage only once every lifter has counted its functions (main analyses for ~40 s first)
|
|
bar.update(done, total) if known == len(procs) else bar.update(0, 0)
|
|
time.sleep(0.5)
|
|
bar.finish()
|
|
failed = False
|
|
for name, log, p in procs:
|
|
code = p.wait()
|
|
log.close()
|
|
lines = (out / f'{name}.log').read_text(errors='replace').splitlines()
|
|
summary = [l for l in lines if 'progress' not in l]
|
|
print(f'{name}: exit {code}; ' + ' | '.join(summary[-2:]))
|
|
if code != 0: # a crash leaves no summary: what it was doing last, and what the code means
|
|
if code & 0xFFFFFFFF in CRASHES:
|
|
print(f' {name}: {CRASHES[code & 0xFFFFFFFF]} ({code & 0xFFFFFFFF:#x})')
|
|
for l in lines[-6:]:
|
|
print(f' {name} | {l}')
|
|
print(f' {name}: the whole log is {out / f"{name}.log"}')
|
|
failed |= code != 0
|
|
print(f'{time.time() - t0:.0f}s')
|
|
sys.exit(1 if failed else 0)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|