#!/usr/bin/env python3 import subprocess, os, glob, sys, time, struct RPP = "/home/m/soothe-bt/render_short.rpp" OUT = "/home/m/soothe-bt/out_rt.wav" CAND = [0x2370040, 0x1580040] proc = subprocess.Popen(['reaper', '-nosplash', '-renderproject', RPP], stdout=open('/tmp/rtver.log', 'w'), stderr=subprocess.STDOUT) print('reaper', proc.pid, flush=True) def find_hosts(): out = [] for p in glob.glob('/proc/[0-9]*'): try: pid = int(os.path.basename(p)) m = open(f'/proc/{pid}/maps').read() c = open(f'/proc/{pid}/cmdline', 'rb').read().decode('utf8', 'replace') if 'soothe2' in m and 'yabridge-host.exe' in c: out.append(pid) except Exception: pass return out def out_growth(): try: return os.path.getsize(OUT) except Exception: return 0 t0 = time.time() hosts = None base_sz = out_growth() while time.time() - t0 < 60: hosts = find_hosts() if hosts: break time.sleep(0.2) print('hosts:', hosts, flush=True) grew = 0 while time.time() - t0 < 120: s = out_growth() if s > base_sz: grew = s; break if proc.poll() is not None: print('reaper exited early', flush=True); break time.sleep(0.3) print('out:', grew, flush=True) if not hosts or grew == 0: proc.kill(); sys.exit('fail') time.sleep(0.5) for pid in hosts: mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY) for a in CAND: try: d = os.pread(mem, 0x300, a) except Exception as e: print(pid, hex(a), 'err', e, flush=True); continue # ?possibly the object base is NOT a; our signature matched offset at +0x19c. # Re-scan around a for the exact int field to find true object base. # fields: N at base+0x19c, N at +0x1a0, float win at +0x1a4, int ups at +0x1ac # find qword-aligned true base by searching +0x1ac==4 in window f4 = struct.unpack('<%dI' % (len(d) // 4), d) bases = [] for o in range(0, len(f4) - (0x1ac // 4 + 1)): if f4[o + 0x1ac // 4] == 4 and f4[o + 0x19c // 4] in (2048, 4096) \ and f4[o + 0x1a0 // 4] == f4[o + 0x19c // 4]: bases.append(o * 4) print(pid, hex(a), 'true-base candidates:', [hex(a + b) for b in bases[:6]], flush=True) for b in bases[:1]: o = b vptr = struct.unpack_from('